Browse Source

Merge branch 'master' into ios-tvos-updates

pull/14196/head
Max Katz 2 years ago
parent
commit
7f8092c523
  1. 2
      Directory.Build.props
  2. 4
      nukebuild/ApiDiffHelper.cs
  3. 8
      packages/Avalonia/Avalonia.csproj
  4. 2
      packages/Avalonia/Avalonia.props
  5. 3
      samples/ControlCatalog/Pages/AdornerLayerPage.xaml
  6. 3
      samples/ControlCatalog/Pages/PlatformInfoPage.xaml
  7. 2
      samples/Directory.Build.props
  8. 2
      src/Android/Avalonia.Android/AndroidPlatform.cs
  9. 52
      src/Android/Avalonia.Android/AndroidRuntimePlatform.cs
  10. 15
      src/Avalonia.Base/Media/PathMarkupParser.cs
  11. 29
      src/Avalonia.Base/Media/TextFormatting/BidiReorderer.cs
  12. 8
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  13. 6
      src/Avalonia.Base/Platform/IRuntimePlatform.cs
  14. 2
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  15. 13
      src/Avalonia.Controls/AppBuilder.cs
  16. 27
      src/Avalonia.Controls/Application.cs
  17. 134
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  18. 3
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
  19. 1
      src/Avalonia.Controls/GridSplitter.cs
  20. 1
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  21. 7
      src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml
  22. 41
      src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
  23. 25
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  24. 6
      src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs
  25. 5
      src/Browser/Avalonia.Browser/Interop/AvaloniaModule.cs
  26. 4
      src/Browser/Avalonia.Browser/webapp/modules/avalonia/caniuse.ts
  27. 3
      src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
  28. 2
      src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
  29. 15
      src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
  30. 17
      src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props
  31. 22
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs
  32. 7
      src/Tizen/Avalonia.Tizen/TizenApplicationExtensions.cs
  33. 38
      src/Tizen/Avalonia.Tizen/TizenRuntimePlatform.cs
  34. 19
      src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
  35. 2
      src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs
  36. 2
      src/tools/Avalonia.Designer.HostApp/Program.cs
  37. 19
      tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs
  38. 23
      tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
  39. 2
      tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs
  40. 29
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

2
Directory.Build.props

@ -2,7 +2,7 @@
<Import Project="$(MSBuildThisFileDirectory)/build/AvaloniaPublicKey.props"/>
<PropertyGroup>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netstandard2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<!-- https://github.com/dotnet/msbuild/issues/2661 -->
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>

4
nukebuild/ApiDiffHelper.cs

@ -105,7 +105,9 @@ public static class ApiDiffHelper
{
// We use StartsWith below comparing these tfm, as we ignore platform versions (like, net6.0-ios16.1)
("net6.0-android", "net7.0-android"),
("net6.0-ios", "net7.0-ios")
("net6.0-ios", "net7.0-ios"),
// Designer was moved from netcoreapp to netstandard
("netcoreapp2.0", "netstandard2.0")
};
public static async Task ValidatePackage(

8
packages/Avalonia/Avalonia.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0;net461;netcoreapp2.0</TargetFrameworks>
<TargetFrameworks>net6.0;netstandard2.0;net461</TargetFrameworks>
<PackageId>Avalonia</PackageId>
</PropertyGroup>
@ -27,11 +27,11 @@
</PropertyGroup>
<Target Name="AddDesignerHostAppsToPackage" BeforeTargets="GenerateNuspec">
<MSBuild Projects="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj" Properties="Configuration=$(Configuration);&#xA; Platform=$(Platform)" />
<MSBuild Projects="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj" Properties="Configuration=$(Configuration);&#xA;Platform=$(Platform)" />
<ItemGroup>
<_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netcoreapp2.0/Avalonia.Designer.HostApp.dll">
<PackagePath>tools/netcoreapp2.0/designer</PackagePath>
<_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netstandard2.0/Avalonia.Designer.HostApp.dll">
<PackagePath>tools/netstandard2.0/designer</PackagePath>
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>

2
packages/Avalonia/Avalonia.props

@ -1,6 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>

3
samples/ControlCatalog/Pages/AdornerLayerPage.xaml

@ -50,7 +50,8 @@
Background="Cyan"
IsHitTestVisible="False"
Opacity="0.3"
IsVisible="True">
IsVisible="True"
AdornerLayer.IsClipEnabled="False">
<Line StartPoint="-10000,0" EndPoint="10000,0" Stroke="Cyan" StrokeThickness="1" />
<Line StartPoint="-10000,42" EndPoint="10000,42" Stroke="Cyan" StrokeThickness="1" />
<Line StartPoint="0,-10000" EndPoint="0,10000" Stroke="Cyan" StrokeThickness="1" />

3
samples/ControlCatalog/Pages/PlatformInfoPage.xaml

@ -19,6 +19,9 @@
<Border Height="100" Width="100" Background="{OnFormFactor Gray, Mobile=Green}">
<TextBlock Text="Mobile" />
</Border>
<Border Height="100" Width="100" Background="{OnFormFactor Gray, TV=Green}">
<TextBlock Text="Tv" />
</Border>
</StackPanel>
<WrapPanel>
<Border Height="100" Width="100" Background="{OnPlatform Gray, Windows=Green}">

2
samples/Directory.Build.props

@ -2,7 +2,7 @@
<PropertyGroup>
<IsPackable>false</IsPackable>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netstandard2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<LangVersion>11</LangVersion>
<NoWarn>$(NoWarn);CS8002</NoWarn> <!-- ignore signing warnings for samples -->

2
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -20,7 +20,7 @@ namespace Avalonia
public static AppBuilder UseAndroid(this AppBuilder builder)
{
return builder
.UseStandardRuntimePlatformSubsystem()
.UseAndroidRuntimePlatformSubsystem()
.UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android")
.UseSkia();
}

52
src/Android/Avalonia.Android/AndroidRuntimePlatform.cs

@ -0,0 +1,52 @@
using System;
using Android.Content.PM;
using Android.Content;
using Avalonia.Platform;
using App = Android.App.Application;
using System.Reflection;
namespace Avalonia
{
internal static class AndroidRuntimePlatformServices
{
public static AppBuilder UseAndroidRuntimePlatformSubsystem(this AppBuilder builder)
{
builder.UseRuntimePlatformSubsystem(() => Register(builder.ApplicationType?.Assembly), nameof(AndroidRuntimePlatform));
return builder;
}
public static void Register(Assembly? assembly = null)
{
AssetLoader.RegisterResUriParsers();
AvaloniaLocator.CurrentMutable
.Bind<IRuntimePlatform>().ToSingleton<AndroidRuntimePlatform>()
.Bind<IAssetLoader>().ToConstant(new StandardAssetLoader(assembly));
}
}
internal class AndroidRuntimePlatform : StandardRuntimePlatform
{
private static readonly Lazy<RuntimePlatformInfo> Info = new(() =>
{
var isDesktop = IsRunningOnDesktop(App.Context);
var isTv = IsRunningOnTv(App.Context);
return new RuntimePlatformInfo
{
IsDesktop = isDesktop,
IsMobile = !isTv && !isDesktop,
IsTV = isTv
};
});
private static bool IsRunningOnDesktop(Context context) =>
context.PackageManager.HasSystemFeature("org.chromium.arc") ||
context.PackageManager.HasSystemFeature("org.chromium.arc.device_management");
private static bool IsRunningOnTv(Context context) =>
context.PackageManager.HasSystemFeature(PackageManager.FeatureLeanback);
public override RuntimePlatformInfo GetRuntimeInfo() => Info.Value;
}
}

15
src/Avalonia.Base/Media/PathMarkupParser.cs

@ -259,7 +259,7 @@ namespace Avalonia.Media
{
ThrowIfDisposed();
_currentPoint = relative
var next = relative
? ReadRelativePoint(ref span, _currentPoint)
: ReadPoint(ref span);
@ -268,14 +268,15 @@ namespace Avalonia.Media
CreateFigure();
}
_geometryContext.LineTo(_currentPoint);
_geometryContext.LineTo(next);
_currentPoint = next;
}
private void AddHorizontalLine(ref ReadOnlySpan<char> span, bool relative)
{
ThrowIfDisposed();
_currentPoint = relative
var next = relative
? new Point(_currentPoint.X + ReadDouble(ref span), _currentPoint.Y)
: _currentPoint.WithX(ReadDouble(ref span));
@ -284,14 +285,15 @@ namespace Avalonia.Media
CreateFigure();
}
_geometryContext.LineTo(_currentPoint);
_geometryContext.LineTo(next);
_currentPoint = next;
}
private void AddVerticalLine(ref ReadOnlySpan<char> span, bool relative)
{
ThrowIfDisposed();
_currentPoint = relative
var next = relative
? new Point(_currentPoint.X, _currentPoint.Y + ReadDouble(ref span))
: _currentPoint.WithY(ReadDouble(ref span));
@ -300,7 +302,8 @@ namespace Avalonia.Media
CreateFigure();
}
_geometryContext.LineTo(_currentPoint);
_geometryContext.LineTo(next);
_currentPoint = next;
}
private void AddCubicBezierCurve(ref ReadOnlySpan<char> span, bool relative)

29
src/Avalonia.Base/Media/TextFormatting/BidiReorderer.cs

@ -30,18 +30,25 @@ namespace Avalonia.Media.TextFormatting
try
{
sbyte? previousLevel = null;
_runs.Add(textRuns.Length);
// Build up the collection of ordered runs.
for (var i = 0; i < textRuns.Length; i++)
{
var textRun = textRuns[i];
_runs[i] = new OrderedBidiRun(i, textRun, GetRunBidiLevel(textRun, flowDirection));
var orderedRun = new OrderedBidiRun(i, textRun, GetRunBidiLevel(textRun, flowDirection, previousLevel));
_runs[i] = orderedRun;
if (i > 0)
{
_runs[i - 1].NextRunIndex = i;
}
previousLevel = orderedRun.Level;
}
// Reorder them into visual order.
@ -72,7 +79,8 @@ namespace Avalonia.Media.TextFormatting
for (var i = 0; i < textRuns.Length; i++)
{
var level = GetRunBidiLevel(textRuns[i], flowDirection);
var level = _runs[i].Level;
if (level > max)
{
max = level;
@ -150,15 +158,26 @@ namespace Avalonia.Media.TextFormatting
}
}
private static sbyte GetRunBidiLevel(TextRun run, FlowDirection flowDirection)
private static sbyte GetRunBidiLevel(TextRun run, FlowDirection flowDirection, sbyte? previousLevel)
{
if (run is ShapedTextRun shapedTextRun)
{
return shapedTextRun.BidiLevel;
}
var defaultLevel = flowDirection == FlowDirection.LeftToRight ? 0 : 1;
return (sbyte)defaultLevel;
var defaultLevel = (sbyte)(flowDirection == FlowDirection.LeftToRight ? 0 : 1);
if (run is TextEndOfLine)
{
return defaultLevel;
}
if(previousLevel is not null)
{
return previousLevel.Value;
}
return defaultLevel;
}
/// <summary>

8
src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs

@ -12,7 +12,7 @@ namespace Avalonia.Media.TextFormatting
internal sealed class TextFormatterImpl : TextFormatter
{
private static readonly char[] s_empty = { ' ' };
private static readonly char[] s_defaultText = new char[TextRun.DefaultTextSourceLength];
private static readonly string s_defaultText = new string('a', TextRun.DefaultTextSourceLength);
[ThreadStatic] private static BidiData? t_bidiData;
[ThreadStatic] private static BidiAlgorithm? t_bidiAlgorithm;
@ -206,9 +206,11 @@ namespace Avalonia.Media.TextFormatting
if (!textRun.Text.IsEmpty)
text = textRun.Text.Span;
else if (textRun.Length == TextRun.DefaultTextSourceLength)
text = s_defaultText;
text = s_defaultText.AsSpan();
else
text = new char[textRun.Length];
{
text = new string('a', textRun.Length).AsSpan();
}
bidiData.Append(text);
}

6
src/Avalonia.Base/Platform/IRuntimePlatform.cs

@ -13,15 +13,17 @@ namespace Avalonia.Platform
public record struct RuntimePlatformInfo
{
public FormFactorType FormFactor => IsDesktop ? FormFactorType.Desktop :
IsMobile ? FormFactorType.Mobile : FormFactorType.Unknown;
IsMobile ? FormFactorType.Mobile : IsTV ? FormFactorType.TV : FormFactorType.Unknown;
public bool IsDesktop { get; set; }
public bool IsMobile { get; set; }
public bool IsTV { get; set; }
}
public enum FormFactorType
{
Unknown,
Desktop,
Mobile
Mobile,
TV
}
}

2
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@ -205,7 +205,7 @@ namespace Avalonia.Rendering.Composition.Server
}
_combinedTransformedClipBounds =
AdornedVisual?._combinedTransformedClipBounds
(AdornerIsClipped ? AdornedVisual?._combinedTransformedClipBounds : null)
?? (Parent?.Effect == null ? Parent?._combinedTransformedClipBounds : null)
?? new Rect(Root!.Size);

13
src/Avalonia.Controls/AppBuilder.cs

@ -6,6 +6,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform;
using Avalonia.Media.Fonts;
using Avalonia.Media;
using Avalonia.Metadata;
namespace Avalonia
{
@ -54,6 +55,11 @@ namespace Avalonia
/// </summary>
public Action? RenderingSubsystemInitializer { get; private set; }
/// <summary>
/// Gets a method to override a lifetime factory.
/// </summary>
public Func<Type, IApplicationLifetime?>? LifetimeOverride { get; private set; }
/// <summary>
/// Gets the name of the currently selected rendering subsystem.
/// </summary>
@ -238,6 +244,13 @@ namespace Avalonia
return Self;
}
[PrivateApi]
public AppBuilder UseLifetimeOverride(Func<Type, IApplicationLifetime?> func)
{
LifetimeOverride = func;
return Self;
}
/// <summary>
/// Configures platform-specific options
/// </summary>

27
src/Avalonia.Controls/Application.cs

@ -42,6 +42,8 @@ namespace Avalonia
private bool _notifyingResourcesChanged;
private Action<IReadOnlyList<IStyle>>? _stylesAdded;
private Action<IReadOnlyList<IStyle>>? _stylesRemoved;
private IApplicationLifetime? _applicationLifetime;
private bool _setupCompleted;
/// <summary>
/// Defines the <see cref="DataContext"/> property.
@ -60,7 +62,7 @@ namespace Avalonia
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs>? ResourcesChanged;
/// <inheritdoc/>
[Obsolete("Cast ApplicationLifetime to IActivatableApplicationLifetime instead.")]
public event EventHandler<UrlOpenedEventArgs>? UrlsOpened;
/// <inheritdoc/>
@ -170,15 +172,28 @@ namespace Avalonia
/// <inheritdoc/>
bool IStyleHost.IsStylesInitialized => _styles != null;
/// <summary>
/// Application lifetime, use it for things like setting the main window and exiting the app from code
/// Currently supported lifetimes are:
/// - <see cref="IClassicDesktopStyleApplicationLifetime"/>
/// - <see cref="ISingleViewApplicationLifetime"/>
/// - <see cref="IControlledApplicationLifetime"/>
/// - <see cref="IActivatableApplicationLifetime"/>
/// </summary>
public IApplicationLifetime? ApplicationLifetime { get; set; }
public IApplicationLifetime? ApplicationLifetime
{
get => _applicationLifetime;
set
{
if (_setupCompleted)
{
throw new InvalidOperationException($"It's not possible to change {nameof(ApplicationLifetime)} after Application was initialized.");
}
_applicationLifetime = value;
}
}
/// <summary>
/// Represents a contract for accessing global platform-specific settings.
@ -207,7 +222,7 @@ namespace Avalonia
/// Initializes the application by loading XAML etc.
/// </summary>
public virtual void Initialize() { }
/// <inheritdoc/>
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
@ -263,13 +278,15 @@ namespace Avalonia
AvaloniaLocator.CurrentMutable.Bind<IGlobalClock>()
.ToConstant(MediaContext.Instance.Clock);
_setupCompleted = true;
}
public virtual void OnFrameworkInitializationCompleted()
{
}
void IApplicationPlatformEvents.RaiseUrlsOpened(string[] urls)
void IApplicationPlatformEvents.RaiseUrlsOpened(string[] urls)
{
UrlsOpened?.Invoke(this, new UrlOpenedEventArgs (urls));
}

134
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@ -8,6 +8,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
using Avalonia.Platform;
using Avalonia.Reactive;
using Avalonia.Threading;
namespace Avalonia.Controls.ApplicationLifetimes
@ -18,38 +19,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
private CancellationTokenSource? _cts;
private bool _isShuttingDown;
private readonly AvaloniaList<Window> _windows = new();
private static ClassicDesktopStyleApplicationLifetime? s_activeLifetime;
static ClassicDesktopStyleApplicationLifetime()
{
Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened);
Window.WindowClosedEvent.AddClassHandler(typeof(Window), OnWindowClosed);
}
private static void OnWindowClosed(object? sender, RoutedEventArgs e)
{
var window = (Window)sender!;
s_activeLifetime?._windows.Remove(window);
s_activeLifetime?.HandleWindowClosed(window);
}
private static void OnWindowOpened(object? sender, RoutedEventArgs e)
{
var window = (Window)sender!;
if (s_activeLifetime is not null && !s_activeLifetime._windows.Contains(window))
{
s_activeLifetime._windows.Add(window);
}
}
public ClassicDesktopStyleApplicationLifetime()
{
if (s_activeLifetime != null)
throw new InvalidOperationException(
"Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed");
s_activeLifetime = this;
}
private CompositeDisposable? _compositeDisposable;
/// <inheritdoc/>
public event EventHandler<ControlledApplicationLifetimeStartupEventArgs>? Startup;
@ -97,9 +67,32 @@ namespace Avalonia.Controls.ApplicationLifetimes
{
return DoShutdown(new ShutdownRequestedEventArgs(), true, false, exitCode);
}
public int Start(string[] args)
internal void SetupCore(string[] args)
{
if (_compositeDisposable is not null)
{
// There could be a case, when lifetime was setup without starting.
// Until developer started it manually later. To avoid API breaking changes, it will execute Setup method twice.
return;
}
_compositeDisposable = new CompositeDisposable(
Window.WindowOpenedEvent.AddClassHandler(typeof(Window), (sender, _) =>
{
var window = (Window)sender!;
if (!_windows.Contains(window))
{
_windows.Add(window);
}
}),
Window.WindowClosedEvent.AddClassHandler(typeof(Window), (sender, _) =>
{
var window = (Window)sender!;
_windows.Remove(window);
HandleWindowClosed(window);
}));
Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args));
var options = AvaloniaLocator.Current.GetService<ClassicDesktopStyleApplicationLifetimeOptions>();
@ -116,9 +109,14 @@ namespace Avalonia.Controls.ApplicationLifetimes
if (lifetimeEvents != null)
lifetimeEvents.ShutdownRequested += OnShutdownRequested;
}
_cts = new CancellationTokenSource();
public int Start(string[] args)
{
SetupCore(args);
_cts = new CancellationTokenSource();
// Note due to a bug in the JIT we wrap this in a method, otherwise MainWindow
// gets stuffed into a local var and can not be GCed until after the program stops.
// this method never exits until program end.
@ -137,8 +135,8 @@ namespace Avalonia.Controls.ApplicationLifetimes
public void Dispose()
{
if (s_activeLifetime == this)
s_activeLifetime = null;
_compositeDisposable?.Dispose();
_compositeDisposable = null;
}
private bool DoShutdown(
@ -206,21 +204,65 @@ namespace Avalonia.Controls.ApplicationLifetimes
namespace Avalonia
{
/// <summary>
/// IClassicDesktopStyleApplicationLifetime related AppBuilder extensions.
/// </summary>
public static class ClassicDesktopStyleApplicationLifetimeExtensions
{
public static int StartWithClassicDesktopLifetime(
this AppBuilder builder, string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
private static ClassicDesktopStyleApplicationLifetime PrepareLifetime(AppBuilder builder, string[] args,
Action<IClassicDesktopStyleApplicationLifetime>? lifetimeBuilder)
{
var lifetime = AvaloniaLocator.Current.GetService<ClassicDesktopStyleApplicationLifetime>();
if (lifetime == null)
{
lifetime = new ClassicDesktopStyleApplicationLifetime();
}
var lifetime = builder.LifetimeOverride?.Invoke(typeof(ClassicDesktopStyleApplicationLifetime)) as ClassicDesktopStyleApplicationLifetime
?? new ClassicDesktopStyleApplicationLifetime();
lifetime.Args = args;
lifetime.ShutdownMode = shutdownMode;
lifetimeBuilder?.Invoke(lifetime);
return lifetime;
}
/// <summary>
/// Setups the Application with a IClassicDesktopStyleApplicationLifetime, but doesn't show the main window and doesn't run application main loop.
/// </summary>
/// <param name="builder">Application builder.</param>
/// <param name="args">Startup arguments.</param>
/// <param name="lifetimeBuilder">Lifetime builder to modify the lifetime before application started.</param>
/// <returns>Exit code.</returns>
public static AppBuilder SetupWithClassicDesktopLifetime(this AppBuilder builder, string[] args,
Action<IClassicDesktopStyleApplicationLifetime>? lifetimeBuilder = null)
{
var lifetime = PrepareLifetime(builder, args, lifetimeBuilder);
lifetime.SetupCore(args);
return builder.SetupWithLifetime(lifetime);
}
/// <summary>
/// Starts the Application with a IClassicDesktopStyleApplicationLifetime, shows main window and runs application main loop.
/// </summary>
/// <param name="builder">Application builder.</param>
/// <param name="args">Startup arguments.</param>
/// <param name="lifetimeBuilder">Lifetime builder to modify the lifetime before application started.</param>
/// <returns>Exit code.</returns>
public static int StartWithClassicDesktopLifetime(
this AppBuilder builder, string[] args,
Action<IClassicDesktopStyleApplicationLifetime>? lifetimeBuilder = null)
{
var lifetime = PrepareLifetime(builder, args, lifetimeBuilder);
builder.SetupWithLifetime(lifetime);
return lifetime.Start(args);
}
/// <summary>
/// Starts the Application with a IClassicDesktopStyleApplicationLifetime, shows main window and runs application main loop.
/// </summary>
/// <param name="builder">Application builder.</param>
/// <param name="args">Startup arguments.</param>
/// <param name="shutdownMode">Lifetime shutdown mode.</param>
/// <returns>Exit code.</returns>
public static int StartWithClassicDesktopLifetime(
this AppBuilder builder, string[] args, ShutdownMode shutdownMode)
{
var lifetime = PrepareLifetime(builder, args, l => l.ShutdownMode = shutdownMode);
builder.SetupWithLifetime(lifetime);
return lifetime.Start(args);
}

3
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs

@ -22,8 +22,7 @@ namespace Avalonia.Controls
public static readonly StyledProperty<int> CaretIndexProperty =
TextBox.CaretIndexProperty.AddOwner<AutoCompleteBox>(new(
defaultValue: 0,
defaultBindingMode:BindingMode.TwoWay,
coerce: TextBox.CoerceCaretIndex));
defaultBindingMode:BindingMode.TwoWay));
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<AutoCompleteBox>();

1
src/Avalonia.Controls/GridSplitter.cs

@ -341,6 +341,7 @@ namespace Avalonia.Controls
_resizeData.Adorner = new PreviewAdorner(builtPreviewContent);
AdornerLayer.SetAdornedElement(_resizeData.Adorner, this);
AdornerLayer.SetIsClipEnabled(_resizeData.Adorner, false);
adornerLayer.Children.Add(_resizeData.Adorner);

1
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -174,7 +174,6 @@ namespace Avalonia.Controls.Primitives
}
SetAdornedElement(adorner, visual);
SetIsClipEnabled(adorner, false);
((ISetLogicalParent) adorner).SetParent(visual);
layer.Children.Add(adorner);

7
src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml

@ -7,13 +7,12 @@
<TreeView Name="tree"
BorderThickness="0"
ItemsSource="{Binding Nodes}"
SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
SelectedItem="{Binding SelectedNode, Mode=TwoWay}"
PointerMoved="UpdateAdorner">
<TreeView.DataTemplates>
<TreeDataTemplate DataType="vm:TreeNode"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Spacing="8"
PointerEntered="AddAdorner"
PointerExited="RemoveAdorner">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Type}" FontWeight="{Binding FontWeight}"/>
<TextBlock Text="{Binding Classes}"/>
<TextBlock Foreground="Gray" Text="{Binding ElementName}"/>

41
src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs

@ -1,14 +1,11 @@
using System;
using System.Diagnostics;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Primitives;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.Views
{
@ -16,6 +13,7 @@ namespace Avalonia.Diagnostics.Views
{
private readonly Panel _adorner;
private AdornerLayer? _currentLayer;
private TreeViewItem? _hovered;
private TreeView _tree;
public TreePageView()
@ -39,6 +37,11 @@ namespace Avalonia.Diagnostics.Views
AdornerLayer.SetIsClipEnabled(_adorner, false);
}
private static Thickness InvertThickness(Thickness input)
{
return new Thickness(-input.Left, -input.Top, -input.Right, -input.Bottom);
}
protected void AddAdorner(object? sender, PointerEventArgs e)
{
var node = (TreeNode?)((Control)sender!).DataContext;
@ -80,11 +83,6 @@ namespace Avalonia.Diagnostics.Views
}
}
private static Thickness InvertThickness(Thickness input)
{
return new Thickness(-input.Left, -input.Top, -input.Right, -input.Bottom);
}
protected void RemoveAdorner(object? sender, PointerEventArgs e)
{
foreach (var border in _adorner.Children.OfType<Border>())
@ -98,6 +96,31 @@ namespace Avalonia.Diagnostics.Views
_currentLayer = null;
}
protected void UpdateAdorner(object? sender, PointerEventArgs e)
{
if (e.Source is not StyledElement source)
{
return;
}
var item = source.FindLogicalAncestorOfType<TreeViewItem>();
if (item == _hovered)
{
return;
}
RemoveAdorner(sender, e);
if (item is null || item.TreeViewOwner != _tree)
{
_hovered = null;
return;
}
_hovered = item;
AddAdorner(item, e);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

25
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@ -13,20 +13,19 @@ namespace Avalonia
builder
.UseStandardRuntimePlatformSubsystem()
.UseWindowingSubsystem(() =>
{
var platform = AvaloniaNativePlatform.Initialize(
AvaloniaLocator.Current.GetService<AvaloniaNativePlatformOptions>() ??
new AvaloniaNativePlatformOptions());
{
var platform = AvaloniaNativePlatform.Initialize(
AvaloniaLocator.Current.GetService<AvaloniaNativePlatformOptions>() ??
new AvaloniaNativePlatformOptions());
builder.AfterSetup (x=>
{
platform.SetupApplicationName();
platform.SetupApplicationMenuExporter();
});
});
AvaloniaLocator.CurrentMutable.Bind<ClassicDesktopStyleApplicationLifetime>()
.ToConstant(new MacOSClassicDesktopStyleApplicationLifetime());
builder.AfterSetup (x=>
{
platform.SetupApplicationName();
platform.SetupApplicationMenuExporter();
});
})
.UseLifetimeOverride(type => type == typeof(ClassicDesktopStyleApplicationLifetime)
? new MacOSClassicDesktopStyleApplicationLifetime() : null);
return builder;
}

6
src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs

@ -30,10 +30,12 @@ internal class BrowserRuntimePlatform : StandardRuntimePlatform
private static readonly Lazy<RuntimePlatformInfo> Info = new(() =>
{
var isMobile = AvaloniaModule.IsMobile();
var isTv = AvaloniaModule.IsTv();
var result = new RuntimePlatformInfo
{
IsMobile = isMobile,
IsDesktop = !isMobile
IsMobile = isMobile && !isTv,
IsDesktop = !isMobile && !isTv,
IsTV = isTv
};
return result;

5
src/Browser/Avalonia.Browser/Interop/AvaloniaModule.cs

@ -33,7 +33,10 @@ internal static partial class AvaloniaModule
[JSImport("Caniuse.isMobile", AvaloniaModule.MainModuleName)]
public static partial bool IsMobile();
[JSImport("Caniuse.isTv", AvaloniaModule.MainModuleName)]
public static partial bool IsTv();
[JSImport("registerServiceWorker", AvaloniaModule.MainModuleName)]
public static partial void RegisterServiceWorker(string path, string? scope);
}

4
src/Browser/Avalonia.Browser/webapp/modules/avalonia/caniuse.ts

@ -14,4 +14,8 @@ export class Caniuse {
const regex2 = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw(n|u)|c55\/|capi|ccwa|cdm|cell|chtm|cldc|cmd|co(mp|nd)|craw|da(it|ll|ng)|dbte|dcs|devi|dica|dmob|do(c|p)o|ds(12|d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(|_)|g1 u|g560|gene|gf5|gmo|go(\.w|od)|gr(ad|un)|haie|hcit|hd(m|p|t)|hei|hi(pt|ta)|hp( i|ip)|hsc|ht(c(| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i(20|go|ma)|i230|iac( ||\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|[a-w])|libw|lynx|m1w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|mcr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|([1-8]|c))|phil|pire|pl(ay|uc)|pn2|po(ck|rt|se)|prox|psio|ptg|qaa|qc(07|12|21|32|60|[2-7]|i)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h|oo|p)|sdk\/|se(c(|0|1)|47|mc|nd|ri)|sgh|shar|sie(|m)|sk0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h|v|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl|tdg|tel(i|m)|tim|tmo|to(pl|sh)|ts(70|m|m3|m5)|tx9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas|your|zeto|zte/i;
return regex1.test(userAgent) || regex2.test(userAgent.substr(0, 4));
}
public static isTv(): boolean {
return navigator.userAgent.includes("SmartTV");
}
}

3
src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs

@ -29,9 +29,6 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput
{
var fd = libinput_get_fd(ctx);
var timeval = stackalloc IntPtr[2];
foreach (var f in Directory.GetFiles("/dev/input", "event*"))
libinput_path_add_device(ctx, f);
while (true)

2
src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs

@ -121,7 +121,7 @@ namespace Avalonia.LinuxFramebuffer.Output
// prepare for the new ioctl call
var handles = new uint[] {handle, 0, 0, 0};
var pitches = new uint[] {stride, 0, 0, 0};
var offsets = Array.Empty<uint>();
var offsets = new uint[4];
var ret = drmModeAddFB2(_card.Fd, w, h, format, handles, pitches,
offsets, out var fbHandle, 0);

15
src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj

@ -5,22 +5,11 @@
<IsPackable>true</IsPackable>
<DefineConstants>$(DefineConstants);XAMLX_INTERNAL;XAML_RUNTIME_LOADER</DefineConstants>
</PropertyGroup>
<!--Disable Net Perf. analyzer for submodule to avoid commit issue -->
<PropertyGroup>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
</PropertyGroup>
<Import Project="IncludeXamlIlSre.props" />
<ItemGroup>
<Compile Include="..\..\Avalonia.Base\Utilities\StringBuilderCache.cs" Link="Utilities\StringBuilderCache.cs" />
<Compile Include="..\..\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="TrimmingAttributes.cs" Visible="False" />
<Compile Include="..\..\Shared\IsExternalInit.cs" Link="Compatibility\IsExternalInit.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
</ItemGroup>
<Import Project="IncludeXamlIlSre.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\TrimmingEnable.props" />
<Import Project="..\..\..\build\SourceGenerators.props" />

17
src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props

@ -1,9 +1,26 @@
<Project>
<!--Disable Net Perf. analyzer for submodule to avoid commit issue -->
<PropertyGroup>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup>
<None Remove="$(MSBuildThisFileDirectory)\xamlil.github\**\*.*" />
<Content Remove="$(MSBuildThisFileDirectory)\xamlil.github\**\*.*" />
<Compile Remove="$(MSBuildThisFileDirectory)\xamlil.github\**\*.*" />
<Compile Include="$(MSBuildThisFileDirectory)\xamlil.github\src\XamlX\**\*.cs" />
<Compile Remove="$(MSBuildThisFileDirectory)\xamlil.github\**\obj\**\*.cs" />
<!-- Polyfills used by XamlX, but re-imported from this repository -->
<Compile Include="$(MSBuildThisFileDirectory)\..\..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" Visible="False" />
<Compile Include="$(MSBuildThisFileDirectory)\..\..\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="TrimmingAttributes.cs" Visible="False" />
<Compile Include="$(MSBuildThisFileDirectory)\..\..\Shared\IsExternalInit.cs" Link="Compatibility\IsExternalInit.cs" />
<!-- Utilities used by XamlX Avalonia SRE -->
<Compile Include="$(MSBuildThisFileDirectory)\..\..\Avalonia.Base\Utilities\StringBuilderCache.cs" Link="Utilities\StringBuilderCache.cs" />
</ItemGroup>
<ItemGroup Condition="!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')))">
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
</ItemGroup>
</Project>

22
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs

@ -4,6 +4,7 @@ using Avalonia.Platform;
namespace Avalonia.Markup.Xaml.MarkupExtensions;
/// <inheritdoc/>
public sealed class OnFormFactorExtension : OnFormFactorExtensionBase<object, On>
{
public OnFormFactorExtension()
@ -22,6 +23,7 @@ public sealed class OnFormFactorExtension : OnFormFactorExtensionBase<object, On
}
}
/// <inheritdoc/>
public sealed class OnFormFactorExtension<TReturn> : OnFormFactorExtensionBase<TReturn, On<TReturn>>
{
public OnFormFactorExtension()
@ -40,18 +42,38 @@ public sealed class OnFormFactorExtension<TReturn> : OnFormFactorExtensionBase<T
}
}
/// <summary>
/// Provides form factor-specific value for T for the current target device.
/// This extension defines "form-factor" as a "device type" rather than "screen type".
/// </summary>
public abstract class OnFormFactorExtensionBase<TReturn, TOn> : IAddChild<TOn>
where TOn : On<TReturn>
{
/// <summary>
/// Gets or sets the value applied by default.
/// If not set, default(TReturn) is assigned to the value.
/// </summary>
[MarkupExtensionDefaultOption]
public TReturn? Default { get; set; }
/// <summary>
/// Gets or sets the value applied on desktop systems.
/// </summary>
[MarkupExtensionOption(FormFactorType.Desktop)]
public TReturn? Desktop { get; set; }
/// <summary>
/// Gets or sets the value applied on mobile systems.
/// </summary>
[MarkupExtensionOption(FormFactorType.Mobile)]
public TReturn? Mobile { get; set; }
/// <summary>
/// Gets or sets the value applied on TV systems.
/// </summary>
[MarkupExtensionOption(FormFactorType.TV)]
public TReturn? TV { get; set; }
// Required for the compiler, will be replaced with actual method compile time.
public object ProvideValue() { return this; }
void IAddChild<TOn>.AddChild(TOn child) {}

7
src/Tizen/Avalonia.Tizen/TizenApplicationExtensions.cs

@ -1,4 +1,7 @@
namespace Avalonia.Tizen;
using System.Reflection;
using Avalonia.Platform;
namespace Avalonia.Tizen;
/// <summary>
/// Extension to setup app builder with tizen backend
@ -13,8 +16,8 @@ public static class TizenApplicationExtensions
public static AppBuilder UseTizen(this AppBuilder builder)
{
return builder
.UseTizenRuntimePlatformSubsystem()
.UseWindowingSubsystem(TizenPlatform.Initialize, "Tizen")
.UseStandardRuntimePlatformSubsystem()
.UseSkia();
}
}

38
src/Tizen/Avalonia.Tizen/TizenRuntimePlatform.cs

@ -0,0 +1,38 @@
using System.Reflection;
using Avalonia.Platform;
namespace Avalonia.Tizen;
internal static class TizenRuntimePlatformServices
{
public static AppBuilder UseTizenRuntimePlatformSubsystem(this AppBuilder builder)
{
builder.UseRuntimePlatformSubsystem(() => Register(builder.ApplicationType?.Assembly), nameof(TizenRuntimePlatform));
return builder;
}
public static void Register(Assembly? assembly = null)
{
AssetLoader.RegisterResUriParsers();
AvaloniaLocator.CurrentMutable
.Bind<IRuntimePlatform>().ToSingleton<TizenRuntimePlatform>()
.Bind<IAssetLoader>().ToConstant(new StandardAssetLoader(assembly));
}
}
internal class TizenRuntimePlatform : StandardRuntimePlatform
{
private static readonly Lazy<RuntimePlatformInfo> Info = new(() =>
{
global::Tizen.System.Information.TryGetValue("http://tizen.org/feature/profile", out string profile);
return new RuntimePlatformInfo
{
IsMobile = profile.Equals("mobile", StringComparison.OrdinalIgnoreCase),
IsTV = profile.Equals("tv", StringComparison.OrdinalIgnoreCase),
IsDesktop = false
};
});
public override RuntimePlatformInfo GetRuntimeInfo() => Info.Value;
}

19
src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj

@ -1,31 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
<TargetFrameworks>net461;netstandard2.0;net6.0</TargetFrameworks>
<DefineConstants>$(DefineConstants);XAMLX_INTERNAL</DefineConstants>
</PropertyGroup>
<!--Disable Net Perf. analyzer for submodule to avoid commit issue -->
<PropertyGroup>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
</ItemGroup>
<Import Project="..\..\..\src\Markup\Avalonia.Markup.Xaml.Loader\IncludeXamlIlSre.props" />
<ItemGroup>
<Compile Include="..\..\..\src\Markup\Avalonia.Markup.Xaml.Loader\CompilerExtensions\**\*.cs" />
<Compile Include="..\..\..\src\Markup\Avalonia.Markup.Xaml.Loader\AvaloniaXamlIlRuntimeCompiler.cs" />
<Compile Include="..\..\..\src\Markup\Avalonia.Markup.Xaml.Loader\CompilerDynamicDependencies.cs" />
<Compile Include="..\..\Avalonia.Base\Utilities\StringBuilderCache.cs" Link="Utilities\StringBuilderCache.cs" />
<Compile Include="..\..\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="TrimmingAttributes.cs" Visible="False" />
<Compile Include="..\..\Shared\IsExternalInit.cs" Link="Compatibility\IsExternalInit.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.22045.20230930" />
</ItemGroup>
<Import Project="..\..\..\build\NetFX.props" />
<Import Project="..\..\..\src\Markup\Avalonia.Markup.Xaml.Loader\IncludeXamlIlSre.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\TrimmingEnable.props" />
</Project>

2
src/tools/Avalonia.Designer.HostApp/DesignXamlLoader.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
@ -9,6 +10,7 @@ using Avalonia.Markup.Xaml.XamlIl;
namespace Avalonia.Designer.HostApp;
[RequiresUnreferencedCode(XamlX.TrimmingMessages.DynamicXamlReference)]
class DesignXamlLoader : AvaloniaXamlLoader.IRuntimeXamlLoader
{
public object Load(RuntimeXamlLoaderDocument document, RuntimeXamlLoaderConfiguration configuration)

2
src/tools/Avalonia.Designer.HostApp/Program.cs

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using Avalonia.DesignerSupport;
@ -6,6 +7,7 @@ using Avalonia.Markup.Xaml;
namespace Avalonia.Designer.HostApp
{
[RequiresUnreferencedCode(XamlX.TrimmingMessages.DynamicXamlReference)]
class Program
{
#if NETFRAMEWORK

19
tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs

@ -319,5 +319,24 @@ namespace Avalonia.Base.UnitTests.Media
Assert.IsType<ArcSegment>(arcSegment);
}
}
[Fact]
public void Should_Handle_StartPoint_After_Empty_Figure()
{
var pathGeometry = new PathGeometry();
using var context = new PathGeometryContext(pathGeometry);
using var parser = new PathMarkupParser(context);
parser.Parse("M50,50z l -5,-5");
Assert.Equal(2, pathGeometry.Figures.Count);
var firstFigure = pathGeometry.Figures[0];
Assert.Equal(new Point(50, 50), firstFigure.StartPoint);
var secondFigure = pathGeometry.Figures[1];
Assert.Equal(new Point(50, 50), secondFigure.StartPoint);
}
}
}

23
tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs

@ -31,6 +31,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(new TestServices(dispatcherImpl: new ManagedDispatcherImpl(null))))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
Dispatcher.UIThread.Post(() => lifetime.Shutdown(1337));
var exitCode = lifetime.Start(Array.Empty<string>());
@ -45,6 +47,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
var windows = new List<Window> { new Window(), new Window(), new Window(), new Window() };
foreach (var window in windows)
@ -65,6 +69,7 @@ namespace Avalonia.Controls.UnitTests
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown;
lifetime.SetupCore(Array.Empty<string>());
var hasExit = false;
@ -99,6 +104,7 @@ namespace Avalonia.Controls.UnitTests
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose;
lifetime.SetupCore(Array.Empty<string>());
var hasExit = false;
@ -127,6 +133,7 @@ namespace Avalonia.Controls.UnitTests
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.ShutdownMode = ShutdownMode.OnLastWindowClose;
lifetime.SetupCore(Array.Empty<string>());
var hasExit = false;
@ -156,6 +163,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
var window = new Window();
window.Show();
@ -170,6 +179,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
var window = new Window();
window.Show();
@ -188,6 +199,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
var window = new Window();
window.Show();
@ -213,6 +226,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(services))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
var window = new Window();
window.Show();
@ -261,6 +276,7 @@ namespace Avalonia.Controls.UnitTests
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose;
lifetime.SetupCore(Array.Empty<string>());
var hasExit = false;
@ -298,6 +314,7 @@ namespace Avalonia.Controls.UnitTests
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.ShutdownMode = ShutdownMode.OnLastWindowClose;
lifetime.SetupCore(Array.Empty<string>());
var hasExit = false;
@ -336,6 +353,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
var hasExit = false;
lifetime.Exit += (_, _) => hasExit = true;
@ -369,6 +388,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow.With(dispatcherImpl: CreateDispatcherWithInstantMainLoop())))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
var hasExit = false;
lifetime.Exit += (_, _) => hasExit = true;
@ -402,6 +423,8 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.StyledWindow))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.SetupCore(Array.Empty<string>());
var hasExit = false;
lifetime.Exit += (_, _) => hasExit = true;

2
tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs

@ -19,7 +19,7 @@ namespace Avalonia.DesignerSupport.Tests
{
public class DesignerSupportTests
{
private const string DesignerAppPath = "../../../../../src/tools/Avalonia.Designer.HostApp/bin/$BUILD/netcoreapp2.0/Avalonia.Designer.HostApp.dll";
private const string DesignerAppPath = "../../../../../src/tools/Avalonia.Designer.HostApp/bin/$BUILD/netstandard2.0/Avalonia.Designer.HostApp.dll";
private readonly Xunit.Abstractions.ITestOutputHelper outputHelper;
public DesignerSupportTests(Xunit.Abstractions.ITestOutputHelper outputHelper)

29
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -135,6 +135,35 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
[Fact]
public void Should_Format_TextLine_With_Non_Text_TextRuns_RightToLeft()
{
using (Start())
{
var defaultProperties =
new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Black);
var textSource = new TextSourceWithDummyRuns(defaultProperties);
var formatter = new TextFormatterImpl();
var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(FlowDirection.RightToLeft, TextAlignment.Left, true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
Assert.NotNull(textLine);
Assert.Equal(5, textLine.TextRuns.Count);
Assert.Equal(14, textLine.Length);
var second = textLine.TextRuns[1] as ShapedTextRun;
Assert.NotNull(second);
Assert.Equal("Hello".AsMemory(), second.Text);
}
}
[Fact]
public void Should_Format_TextRuns_With_TextRunStyles()
{

Loading…
Cancel
Save