diff --git a/Directory.Build.props b/Directory.Build.props
index c19a55e8ea..ec7a7ed18b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -2,7 +2,7 @@
$(MSBuildThisFileDirectory)build-intermediate/nuget
- $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll
+ $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netstandard2.0\Avalonia.Designer.HostApp.dll
false
false
diff --git a/nukebuild/ApiDiffHelper.cs b/nukebuild/ApiDiffHelper.cs
index 946477d287..9af49533a2 100644
--- a/nukebuild/ApiDiffHelper.cs
+++ b/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(
diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj
index 333b21718d..6a3b06ee6c 100644
--- a/packages/Avalonia/Avalonia.csproj
+++ b/packages/Avalonia/Avalonia.csproj
@@ -1,6 +1,6 @@
- net6.0;netstandard2.0;net461;netcoreapp2.0
+ net6.0;netstandard2.0;net461
Avalonia
@@ -27,11 +27,11 @@
-
+
- <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netcoreapp2.0/Avalonia.Designer.HostApp.dll">
- tools/netcoreapp2.0/designer
+ <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netstandard2.0/Avalonia.Designer.HostApp.dll">
+ tools/netstandard2.0/designer
false
None
diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props
index 19190dd43a..259349452b 100644
--- a/packages/Avalonia/Avalonia.props
+++ b/packages/Avalonia/Avalonia.props
@@ -1,6 +1,6 @@
- $(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll
+ $(MSBuildThisFileDirectory)\..\tools\netstandard2.0\designer\Avalonia.Designer.HostApp.dll
$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe
$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll
false
diff --git a/samples/ControlCatalog/Pages/AdornerLayerPage.xaml b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml
index 598844d695..7501c80940 100644
--- a/samples/ControlCatalog/Pages/AdornerLayerPage.xaml
+++ b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml
@@ -50,7 +50,8 @@
Background="Cyan"
IsHitTestVisible="False"
Opacity="0.3"
- IsVisible="True">
+ IsVisible="True"
+ AdornerLayer.IsClipEnabled="False">
diff --git a/samples/ControlCatalog/Pages/PlatformInfoPage.xaml b/samples/ControlCatalog/Pages/PlatformInfoPage.xaml
index f02741d2da..22c47f6bef 100644
--- a/samples/ControlCatalog/Pages/PlatformInfoPage.xaml
+++ b/samples/ControlCatalog/Pages/PlatformInfoPage.xaml
@@ -19,6 +19,9 @@
+
+
+
diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props
index c8216c3031..680d6d2b89 100644
--- a/samples/Directory.Build.props
+++ b/samples/Directory.Build.props
@@ -2,7 +2,7 @@
false
- $(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netcoreapp2.0\Avalonia.Designer.HostApp.dll
+ $(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netstandard2.0\Avalonia.Designer.HostApp.dll
false
11
$(NoWarn);CS8002
diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs
index d8202064e7..b991d8067f 100644
--- a/src/Android/Avalonia.Android/AndroidPlatform.cs
+++ b/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();
}
diff --git a/src/Android/Avalonia.Android/AndroidRuntimePlatform.cs b/src/Android/Avalonia.Android/AndroidRuntimePlatform.cs
new file mode 100644
index 0000000000..f38492005a
--- /dev/null
+++ b/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().ToSingleton()
+ .Bind().ToConstant(new StandardAssetLoader(assembly));
+ }
+ }
+
+
+ internal class AndroidRuntimePlatform : StandardRuntimePlatform
+ {
+ private static readonly Lazy 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;
+ }
+}
diff --git a/src/Avalonia.Base/Compatibility/OperatingSystem.cs b/src/Avalonia.Base/Compatibility/OperatingSystem.cs
index eac199b32f..ad5fe0246a 100644
--- a/src/Avalonia.Base/Compatibility/OperatingSystem.cs
+++ b/src/Avalonia.Base/Compatibility/OperatingSystem.cs
@@ -8,7 +8,9 @@ namespace Avalonia.Compatibility
#if NET6_0_OR_GREATER
public static bool IsWindows() => OperatingSystem.IsWindows();
public static bool IsMacOS() => OperatingSystem.IsMacOS();
+ public static bool IsMacCatalyst() => OperatingSystem.IsMacCatalyst();
public static bool IsLinux() => OperatingSystem.IsLinux();
+ public static bool IsFreeBSD() => OperatingSystem.IsFreeBSD();
public static bool IsAndroid() => OperatingSystem.IsAndroid();
public static bool IsIOS() => OperatingSystem.IsIOS();
public static bool IsTvOS() => OperatingSystem.IsTvOS();
@@ -18,10 +20,12 @@ namespace Avalonia.Compatibility
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static bool IsMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
- public static bool IsAndroid() => IsOSPlatform("ANDROID");
- public static bool IsIOS() => IsOSPlatform("IOS");
- public static bool IsTvOS() => IsOSPlatform("TVOS"); // untested
- public static bool IsBrowser() => IsOSPlatform("BROWSER");
+ public static bool IsFreeBSD() => false;
+ public static bool IsAndroid() => false;
+ public static bool IsIOS() => false;
+ public static bool IsMacCatalyst() => false;
+ public static bool IsTvOS() => false;
+ public static bool IsBrowser() => false;
public static bool IsOSPlatform(string platform) => RuntimeInformation.IsOSPlatform(OSPlatform.Create(platform));
#endif
}
diff --git a/src/Avalonia.Base/Media/PathMarkupParser.cs b/src/Avalonia.Base/Media/PathMarkupParser.cs
index 7b9fdf9330..fa790c17c0 100644
--- a/src/Avalonia.Base/Media/PathMarkupParser.cs
+++ b/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 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 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 span, bool relative)
diff --git a/src/Avalonia.Base/Media/TextFormatting/BidiReorderer.cs b/src/Avalonia.Base/Media/TextFormatting/BidiReorderer.cs
index 39ef8cce48..85fea48edb 100644
--- a/src/Avalonia.Base/Media/TextFormatting/BidiReorderer.cs
+++ b/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;
}
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
index 694961cb94..1b477db1a8 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
+++ b/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);
}
diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs
index c6efadc99a..565f44ec40 100644
--- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs
+++ b/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
}
}
diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs
index 7a5c92c774..b72e10c831 100644
--- a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs
+++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs
@@ -1,20 +1,18 @@
-using System;
-using System.Threading;
using Avalonia.Compatibility;
using Avalonia.Metadata;
-using Avalonia.Platform.Internal;
namespace Avalonia.Platform
{
[PrivateApi]
public class StandardRuntimePlatform : IRuntimePlatform
{
- private static readonly RuntimePlatformInfo s_info = new()
+ public virtual RuntimePlatformInfo GetRuntimeInfo() => new()
{
- IsDesktop = OperatingSystemEx.IsWindows() || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsLinux(),
- IsMobile = OperatingSystemEx.IsAndroid() || OperatingSystemEx.IsIOS()
+ IsDesktop = OperatingSystemEx.IsWindows()
+ || OperatingSystemEx.IsMacOS() || OperatingSystemEx.IsMacCatalyst()
+ || OperatingSystemEx.IsLinux() || OperatingSystemEx.IsFreeBSD(),
+ IsMobile = OperatingSystemEx.IsAndroid() || (OperatingSystemEx.IsIOS() && !OperatingSystemEx.IsMacCatalyst()),
+ IsTV = OperatingSystemEx.IsTvOS()
};
-
- public virtual RuntimePlatformInfo GetRuntimeInfo() => s_info;
}
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
index aeb228282e..d7bdde11e9 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
+++ b/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);
diff --git a/src/Avalonia.Controls/AppBuilder.cs b/src/Avalonia.Controls/AppBuilder.cs
index 436e618a6b..3275f35989 100644
--- a/src/Avalonia.Controls/AppBuilder.cs
+++ b/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
///
public Action? RenderingSubsystemInitializer { get; private set; }
+ ///
+ /// Gets a method to override a lifetime factory.
+ ///
+ public Func? LifetimeOverride { get; private set; }
+
///
/// Gets the name of the currently selected rendering subsystem.
///
@@ -238,6 +244,13 @@ namespace Avalonia
return Self;
}
+ [PrivateApi]
+ public AppBuilder UseLifetimeOverride(Func func)
+ {
+ LifetimeOverride = func;
+ return Self;
+ }
+
///
/// Configures platform-specific options
///
diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs
index 44de1c50eb..46b31bff3e 100644
--- a/src/Avalonia.Controls/Application.cs
+++ b/src/Avalonia.Controls/Application.cs
@@ -42,6 +42,8 @@ namespace Avalonia
private bool _notifyingResourcesChanged;
private Action>? _stylesAdded;
private Action>? _stylesRemoved;
+ private IApplicationLifetime? _applicationLifetime;
+ private bool _setupCompleted;
///
/// Defines the property.
@@ -60,7 +62,7 @@ namespace Avalonia
///
public event EventHandler? ResourcesChanged;
- ///
+ [Obsolete("Cast ApplicationLifetime to IActivatableApplicationLifetime instead.")]
public event EventHandler? UrlsOpened;
///
@@ -170,15 +172,28 @@ namespace Avalonia
///
bool IStyleHost.IsStylesInitialized => _styles != null;
-
+
///
/// Application lifetime, use it for things like setting the main window and exiting the app from code
/// Currently supported lifetimes are:
/// -
/// -
/// -
+ /// -
///
- 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;
+ }
+ }
///
/// Represents a contract for accessing global platform-specific settings.
@@ -207,7 +222,7 @@ namespace Avalonia
/// Initializes the application by loading XAML etc.
///
public virtual void Initialize() { }
-
+
///
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
@@ -263,13 +278,15 @@ namespace Avalonia
AvaloniaLocator.CurrentMutable.Bind()
.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));
}
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
index f235d0e2cf..2c467853d4 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
+++ b/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 _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;
///
public event EventHandler? 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();
@@ -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
{
+ ///
+ /// IClassicDesktopStyleApplicationLifetime related AppBuilder extensions.
+ ///
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? lifetimeBuilder)
{
- var lifetime = AvaloniaLocator.Current.GetService();
-
- 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;
+ }
+
+ ///
+ /// Setups the Application with a IClassicDesktopStyleApplicationLifetime, but doesn't show the main window and doesn't run application main loop.
+ ///
+ /// Application builder.
+ /// Startup arguments.
+ /// Lifetime builder to modify the lifetime before application started.
+ /// Exit code.
+ public static AppBuilder SetupWithClassicDesktopLifetime(this AppBuilder builder, string[] args,
+ Action? lifetimeBuilder = null)
+ {
+ var lifetime = PrepareLifetime(builder, args, lifetimeBuilder);
+ lifetime.SetupCore(args);
+ return builder.SetupWithLifetime(lifetime);
+ }
+
+ ///
+ /// Starts the Application with a IClassicDesktopStyleApplicationLifetime, shows main window and runs application main loop.
+ ///
+ /// Application builder.
+ /// Startup arguments.
+ /// Lifetime builder to modify the lifetime before application started.
+ /// Exit code.
+ public static int StartWithClassicDesktopLifetime(
+ this AppBuilder builder, string[] args,
+ Action? lifetimeBuilder = null)
+ {
+ var lifetime = PrepareLifetime(builder, args, lifetimeBuilder);
+ builder.SetupWithLifetime(lifetime);
+ return lifetime.Start(args);
+ }
+ ///
+ /// Starts the Application with a IClassicDesktopStyleApplicationLifetime, shows main window and runs application main loop.
+ ///
+ /// Application builder.
+ /// Startup arguments.
+ /// Lifetime shutdown mode.
+ /// Exit code.
+ 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);
}
diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
index bcee7f36e3..05b08af31f 100644
--- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
@@ -22,8 +22,7 @@ namespace Avalonia.Controls
public static readonly StyledProperty CaretIndexProperty =
TextBox.CaretIndexProperty.AddOwner(new(
defaultValue: 0,
- defaultBindingMode:BindingMode.TwoWay,
- coerce: TextBox.CoerceCaretIndex));
+ defaultBindingMode:BindingMode.TwoWay));
public static readonly StyledProperty WatermarkProperty =
TextBox.WatermarkProperty.AddOwner();
diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs
index 234c1ff27a..b9e77ca6db 100644
--- a/src/Avalonia.Controls/GridSplitter.cs
+++ b/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);
diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
index f21b608667..412dd236ff 100644
--- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs
+++ b/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);
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml
index ecdd46dd74..0a94d428c1 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml
@@ -7,13 +7,12 @@
+ SelectedItem="{Binding SelectedNode, Mode=TwoWay}"
+ PointerMoved="UpdateAdorner">
-
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
index b0aea64994..485216234d 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
+++ b/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())
@@ -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();
+ 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);
diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
index ccd358ff8e..1956e214fb 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
@@ -13,20 +13,19 @@ namespace Avalonia
builder
.UseStandardRuntimePlatformSubsystem()
.UseWindowingSubsystem(() =>
- {
- var platform = AvaloniaNativePlatform.Initialize(
- AvaloniaLocator.Current.GetService() ??
- new AvaloniaNativePlatformOptions());
+ {
+ var platform = AvaloniaNativePlatform.Initialize(
+ AvaloniaLocator.Current.GetService() ??
+ new AvaloniaNativePlatformOptions());
- builder.AfterSetup (x=>
- {
- platform.SetupApplicationName();
- platform.SetupApplicationMenuExporter();
- });
- });
-
- AvaloniaLocator.CurrentMutable.Bind()
- .ToConstant(new MacOSClassicDesktopStyleApplicationLifetime());
+ builder.AfterSetup (x=>
+ {
+ platform.SetupApplicationName();
+ platform.SetupApplicationMenuExporter();
+ });
+ })
+ .UseLifetimeOverride(type => type == typeof(ClassicDesktopStyleApplicationLifetime)
+ ? new MacOSClassicDesktopStyleApplicationLifetime() : null);
return builder;
}
diff --git a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs
index eee2b70c9c..8709fd79fd 100644
--- a/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs
+++ b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs
@@ -30,10 +30,12 @@ internal class BrowserRuntimePlatform : StandardRuntimePlatform
private static readonly Lazy 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;
diff --git a/src/Browser/Avalonia.Browser/Interop/AvaloniaModule.cs b/src/Browser/Avalonia.Browser/Interop/AvaloniaModule.cs
index cb7aabbc39..b48a00919c 100644
--- a/src/Browser/Avalonia.Browser/Interop/AvaloniaModule.cs
+++ b/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);
}
diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/caniuse.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/caniuse.ts
index 8fdc3a5c01..54e6900abc 100644
--- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/caniuse.ts
+++ b/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");
+ }
}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
index fba9862a85..1a22bf06e0 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
+++ b/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)
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
index c91498fe05..1cea3ac9ba 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
+++ b/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();
+ var offsets = new uint[4];
var ret = drmModeAddFB2(_card.Fd, w, h, format, handles, pitches,
offsets, out var fbHandle, 0);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
index bb18dd5ee3..532f75e8eb 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -5,22 +5,11 @@
true
$(DefineConstants);XAMLX_INTERNAL;XAML_RUNTIME_LOADER
-
-
- false
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props b/src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props
index 251f84d304..173af923cb 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/IncludeXamlIlSre.props
@@ -1,9 +1,26 @@
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs
index a07595a35b..1ac733a345 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs
@@ -4,6 +4,7 @@ using Avalonia.Platform;
namespace Avalonia.Markup.Xaml.MarkupExtensions;
+///
public sealed class OnFormFactorExtension : OnFormFactorExtensionBase