diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml
index 32ab43f84f..3858eaa6ff 100644
--- a/api/Avalonia.nupkg.xml
+++ b/api/Avalonia.nupkg.xml
@@ -19,6 +19,18 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0001
+ T:Avalonia.Platform.IReadableBitmapWithAlphaImpl
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0001
+ T:Avalonia.Utilities.StringTokenizer
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0001
T:Avalonia.Controls.Primitives.IScrollable
@@ -49,6 +61,18 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0001
+ T:Avalonia.Platform.IReadableBitmapWithAlphaImpl
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0001
+ T:Avalonia.Utilities.StringTokenizer
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0001
T:Avalonia.Controls.Primitives.IScrollable
@@ -73,6 +97,24 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.KeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.TextInput.TextInputMethodClient.ShowInputPanel
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Media.DrawingImage.get_Viewbox
@@ -97,6 +139,12 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Media.Imaging.Bitmap.CopyPixels(Avalonia.Platform.ILockedFramebuffer,Avalonia.Platform.AlphaFormat)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)
@@ -121,6 +169,48 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.GenericTextRunProperties.#ctor(Avalonia.Media.Typeface,Avalonia.Media.FontFeatureCollection,System.Double,Avalonia.Media.TextDecorationCollection,Avalonia.Media.IBrush,Avalonia.Media.IBrush,Avalonia.Media.BaselineAlignment,System.Globalization.CultureInfo)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.GenericTextRunProperties.#ctor(Avalonia.Media.Typeface,System.Double,Avalonia.Media.TextDecorationCollection,Avalonia.Media.IBrush,Avalonia.Media.IBrush,Avalonia.Media.BaselineAlignment,System.Globalization.CultureInfo)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextCollapsingProperties.CreateCollapsedRuns(Avalonia.Media.TextFormatting.TextLine,System.Int32,Avalonia.Media.FlowDirection,Avalonia.Media.TextFormatting.TextRun)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextLayout.#ctor(System.String,Avalonia.Media.Typeface,Avalonia.Media.FontFeatureCollection,System.Double,Avalonia.Media.IBrush,Avalonia.Media.TextAlignment,Avalonia.Media.TextWrapping,Avalonia.Media.TextTrimming,Avalonia.Media.TextDecorationCollection,Avalonia.Media.FlowDirection,System.Double,System.Double,System.Double,System.Double,System.Int32,System.Collections.Generic.IReadOnlyList{Avalonia.Utilities.ValueSpan{Avalonia.Media.TextFormatting.TextRunProperties}})
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextLayout.#ctor(System.String,Avalonia.Media.Typeface,System.Double,Avalonia.Media.IBrush,Avalonia.Media.TextAlignment,Avalonia.Media.TextWrapping,Avalonia.Media.TextTrimming,Avalonia.Media.TextDecorationCollection,Avalonia.Media.FlowDirection,System.Double,System.Double,System.Double,System.Double,System.Int32,System.Collections.Generic.IReadOnlyList{Avalonia.Utilities.ValueSpan{Avalonia.Media.TextFormatting.TextRunProperties}})
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.GlyphTypeface,System.Collections.Generic.IReadOnlyList{Avalonia.Media.FontFeature},System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.GlyphTypeface,System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)
@@ -157,6 +247,12 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Platform.LockedFramebuffer.#ctor(System.IntPtr,Avalonia.PixelSize,System.Int32,Avalonia.Vector,Avalonia.Platform.PixelFormat,System.Action)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)
@@ -181,6 +277,12 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ F:Avalonia.Controls.Documents.Inline.TextDecorationsProperty
+ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
CP0002
F:Avalonia.Controls.TextBlock.LetterSpacingProperty
@@ -223,6 +325,12 @@
baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
+ CP0002
+ M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.Control)
+ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
CP0002
M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})
@@ -259,6 +367,18 @@
baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
+ CP0002
+ M:Avalonia.Controls.Primitives.TextSearch.GetText(Avalonia.Controls.Control)
+ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
+
+ CP0002
+ M:Avalonia.Controls.Primitives.TextSearch.SetText(Avalonia.Controls.Control,System.String)
+ baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net10.0/Avalonia.Controls.dll
+
CP0002
M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)
@@ -343,6 +463,24 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.KeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.TextInput.TextInputMethodClient.ShowInputPanel
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Media.DrawingImage.get_Viewbox
@@ -367,6 +505,12 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Media.Imaging.Bitmap.CopyPixels(Avalonia.Platform.ILockedFramebuffer,Avalonia.Platform.AlphaFormat)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)
@@ -391,6 +535,48 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.GenericTextRunProperties.#ctor(Avalonia.Media.Typeface,Avalonia.Media.FontFeatureCollection,System.Double,Avalonia.Media.TextDecorationCollection,Avalonia.Media.IBrush,Avalonia.Media.IBrush,Avalonia.Media.BaselineAlignment,System.Globalization.CultureInfo)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.GenericTextRunProperties.#ctor(Avalonia.Media.Typeface,System.Double,Avalonia.Media.TextDecorationCollection,Avalonia.Media.IBrush,Avalonia.Media.IBrush,Avalonia.Media.BaselineAlignment,System.Globalization.CultureInfo)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextCollapsingProperties.CreateCollapsedRuns(Avalonia.Media.TextFormatting.TextLine,System.Int32,Avalonia.Media.FlowDirection,Avalonia.Media.TextFormatting.TextRun)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextLayout.#ctor(System.String,Avalonia.Media.Typeface,Avalonia.Media.FontFeatureCollection,System.Double,Avalonia.Media.IBrush,Avalonia.Media.TextAlignment,Avalonia.Media.TextWrapping,Avalonia.Media.TextTrimming,Avalonia.Media.TextDecorationCollection,Avalonia.Media.FlowDirection,System.Double,System.Double,System.Double,System.Double,System.Int32,System.Collections.Generic.IReadOnlyList{Avalonia.Utilities.ValueSpan{Avalonia.Media.TextFormatting.TextRunProperties}})
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextLayout.#ctor(System.String,Avalonia.Media.Typeface,System.Double,Avalonia.Media.IBrush,Avalonia.Media.TextAlignment,Avalonia.Media.TextWrapping,Avalonia.Media.TextTrimming,Avalonia.Media.TextDecorationCollection,Avalonia.Media.FlowDirection,System.Double,System.Double,System.Double,System.Double,System.Int32,System.Collections.Generic.IReadOnlyList{Avalonia.Utilities.ValueSpan{Avalonia.Media.TextFormatting.TextRunProperties}})
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.GlyphTypeface,System.Collections.Generic.IReadOnlyList{Avalonia.Media.FontFeature},System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.GlyphTypeface,System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)
@@ -427,6 +613,12 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Platform.LockedFramebuffer.#ctor(System.IntPtr,Avalonia.PixelSize,System.Int32,Avalonia.Vector,Avalonia.Platform.PixelFormat,System.Action)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)
@@ -451,6 +643,12 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ F:Avalonia.Controls.Documents.Inline.TextDecorationsProperty
+ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
CP0002
F:Avalonia.Controls.TextBlock.LetterSpacingProperty
@@ -493,6 +691,12 @@
baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
+ CP0002
+ M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.Control)
+ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
CP0002
M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})
@@ -529,6 +733,18 @@
baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
+ CP0002
+ M:Avalonia.Controls.Primitives.TextSearch.GetText(Avalonia.Controls.Control)
+ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
+
+ CP0002
+ M:Avalonia.Controls.Primitives.TextSearch.SetText(Avalonia.Controls.Control,System.String)
+ baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll
+ current/Avalonia/lib/net8.0/Avalonia.Controls.dll
+
CP0002
M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)
@@ -619,6 +835,12 @@
baseline/netstandard2.0/Avalonia.Base.dll
target/netstandard2.0/Avalonia.Base.dll
+
+ CP0006
+ M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers,System.Nullable{Avalonia.Input.KeyDeviceType})
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0006
M:Avalonia.Platform.IDrawingContextImpl.PopTextOptions
@@ -667,6 +889,18 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0006
+ P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0006
+ P:Avalonia.Platform.IReadableBitmapImpl.AlphaFormat
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0006
M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)
@@ -727,6 +961,12 @@
baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll
current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll
+
+ CP0006
+ M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers,System.Nullable{Avalonia.Input.KeyDeviceType})
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0006
M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)
@@ -811,6 +1051,18 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0006
+ P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0006
+ P:Avalonia.Platform.IReadableBitmapImpl.AlphaFormat
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0006
M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)
@@ -895,12 +1147,24 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0008
+ T:Avalonia.Platform.IWriteableBitmapImpl
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0008
T:Avalonia.Media.StreamGeometryContext
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0008
+ T:Avalonia.Platform.IWriteableBitmapImpl
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0009
T:Avalonia.Platform.Screen
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index 8fe77a095d..d3750935f9 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -134,6 +134,8 @@
false
false
false
+ true
+ false
@@ -162,6 +164,7 @@
DelaySign="$(DelaySign)"
SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)"
DebuggerLaunch="$(AvaloniaXamlIlDebuggerLaunch)"
+ CreateSourceInfo="$(AvaloniaXamlCreateSourceInfo)"
DefaultCompileBindings="$(AvaloniaUseCompiledBindingsByDefault)"
VerboseExceptions="$(AvaloniaXamlVerboseExceptions)"
AnalyzerConfigFiles="@(EditorConfigFiles)"/>
diff --git a/packages/Avalonia/AvaloniaRules.Project.xml b/packages/Avalonia/AvaloniaRules.Project.xml
index b69ea6de17..0a5c1b8243 100644
--- a/packages/Avalonia/AvaloniaRules.Project.xml
+++ b/packages/Avalonia/AvaloniaRules.Project.xml
@@ -31,6 +31,11 @@
Description="Allow debug XAML compilation"
Category="Debug" />
+
+
The current element.
/// The direction to move.
/// Any key modifiers active at the time of focus.
- void Move(
+ /// The device type used to move the focus.
+ bool Move(
IInputElement element,
NavigationDirection direction,
- KeyModifiers keyModifiers = KeyModifiers.None);
+ KeyModifiers keyModifiers = KeyModifiers.None,
+ KeyDeviceType? deviceType = null);
}
}
diff --git a/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
index 3444a88aba..e5e7eb0699 100644
--- a/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
+++ b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
@@ -98,22 +98,12 @@ namespace Avalonia.Input
return result;
}
- ///
- /// Moves the focus in the specified direction.
- ///
- /// The current element.
- /// The direction to move.
- /// Any key modifiers active at the time of focus.
- public void Move(
+ ///
+ public bool Move(
IInputElement? element,
NavigationDirection direction,
- KeyModifiers keyModifiers = KeyModifiers.None)
- {
- MovePrivate(element, direction, keyModifiers, null);
- }
-
- // TODO12: remove MovePrivate, and make Move return boolean. Or even remove whole KeyboardNavigationHandler.
- private bool MovePrivate(IInputElement? element, NavigationDirection direction, KeyModifiers keyModifiers, KeyDeviceType? deviceType)
+ KeyModifiers keyModifiers = KeyModifiers.None,
+ KeyDeviceType? deviceType = null)
{
var next = GetNextPrivate(element, _owner, direction, deviceType);
@@ -140,7 +130,7 @@ namespace Avalonia.Input
var current = FocusManager.GetFocusManager(e.Source as IInputElement)?.GetFocusedElement();
var direction = (e.KeyModifiers & KeyModifiers.Shift) == 0 ?
NavigationDirection.Next : NavigationDirection.Previous;
- e.Handled = MovePrivate(current, direction, e.KeyModifiers, e.KeyDeviceType);
+ e.Handled = Move(current, direction, e.KeyModifiers, e.KeyDeviceType);
}
else if (e.Key is Key.Left or Key.Right or Key.Up or Key.Down)
{
@@ -153,7 +143,7 @@ namespace Avalonia.Input
Key.Down => NavigationDirection.Down,
_ => throw new ArgumentOutOfRangeException()
};
- e.Handled = MovePrivate(current, direction, e.KeyModifiers, e.KeyDeviceType);
+ e.Handled = Move(current, direction, e.KeyModifiers, e.KeyDeviceType);
}
}
diff --git a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs
index 7f9870315b..36e14cd3fd 100644
--- a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs
+++ b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs
@@ -82,13 +82,6 @@ namespace Avalonia.Input.TextInput
{
SetPreeditText(preeditText);
}
-
- //TODO12: remove
- [Obsolete]
- public virtual void ShowInputPanel()
- {
- RaiseInputPaneActivationRequested();
- }
protected virtual void RaiseTextViewVisualChanged()
{
diff --git a/src/Avalonia.Base/Media/FormattedText.cs b/src/Avalonia.Base/Media/FormattedText.cs
index c285ce2f51..5000acb2c5 100644
--- a/src/Avalonia.Base/Media/FormattedText.cs
+++ b/src/Avalonia.Base/Media/FormattedText.cs
@@ -184,13 +184,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
runProps.Typeface,
- runProps.FontFeatures,
runProps.FontRenderingEmSize,
runProps.TextDecorations,
foregroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- runProps.CultureInfo
+ runProps.CultureInfo,
+ runProps.FontFeatures
);
#pragma warning restore 6506
@@ -240,13 +240,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
runProps.Typeface,
- fontFeatures,
runProps.FontRenderingEmSize,
runProps.TextDecorations,
runProps.ForegroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- runProps.CultureInfo
+ runProps.CultureInfo,
+ fontFeatures
);
#pragma warning restore 6506
@@ -328,14 +328,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
new Typeface(fontFamily, oldTypeface.Style, oldTypeface.Weight),
- runProps.FontFeatures,
runProps.FontRenderingEmSize,
runProps.TextDecorations,
runProps.ForegroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- runProps.CultureInfo
- );
+ runProps.CultureInfo,
+ runProps.FontFeatures);
#pragma warning restore 6506
_latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
@@ -388,13 +387,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
runProps.Typeface,
- runProps.FontFeatures,
emSize,
runProps.TextDecorations,
runProps.ForegroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- runProps.CultureInfo
+ runProps.CultureInfo,
+ runProps.FontFeatures
);
_latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
@@ -451,13 +450,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
runProps.Typeface,
- runProps.FontFeatures,
runProps.FontRenderingEmSize,
runProps.TextDecorations,
runProps.ForegroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- culture
+ culture,
+ runProps.FontFeatures
);
#pragma warning restore 6506
@@ -511,13 +510,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
new Typeface(oldTypeface.FontFamily, oldTypeface.Style, weight),
- runProps.FontFeatures,
runProps.FontRenderingEmSize,
runProps.TextDecorations,
runProps.ForegroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- runProps.CultureInfo
+ runProps.CultureInfo,
+ runProps.FontFeatures
);
#pragma warning restore 6506
_latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, newProps, formatRider.SpanPosition);
@@ -568,13 +567,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
new Typeface(oldTypeface.FontFamily, style, oldTypeface.Weight),
- runProps.FontFeatures,
runProps.FontRenderingEmSize,
runProps.TextDecorations,
runProps.ForegroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- runProps.CultureInfo
+ runProps.CultureInfo,
+ runProps.FontFeatures
);
#pragma warning restore 6506
@@ -625,13 +624,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
typeface,
- runProps.FontFeatures,
runProps.FontRenderingEmSize,
runProps.TextDecorations,
runProps.ForegroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- runProps.CultureInfo
+ runProps.CultureInfo,
+ runProps.FontFeatures
);
#pragma warning restore 6506
@@ -683,13 +682,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties(
runProps.Typeface,
- runProps.FontFeatures,
runProps.FontRenderingEmSize,
textDecorations,
runProps.ForegroundBrush,
runProps.BackgroundBrush,
runProps.BaselineAlignment,
- runProps.CultureInfo
+ runProps.CultureInfo,
+ runProps.FontFeatures
);
#pragma warning restore 6506
diff --git a/src/Avalonia.Base/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs
index 2a1ce15feb..9c0c20d170 100644
--- a/src/Avalonia.Base/Media/Imaging/Bitmap.cs
+++ b/src/Avalonia.Base/Media/Imaging/Bitmap.cs
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using Avalonia.Platform;
@@ -177,7 +176,7 @@ namespace Avalonia.Media.Imaging
public virtual PixelFormat? Format => (PlatformImpl.Item as IReadableBitmapImpl)?.Format;
- public virtual AlphaFormat? AlphaFormat => (PlatformImpl.Item as IReadableBitmapWithAlphaImpl)?.AlphaFormat;
+ public virtual AlphaFormat? AlphaFormat => (PlatformImpl.Item as IReadableBitmapImpl)?.AlphaFormat;
private protected unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride,
ILockedFramebuffer fb)
@@ -237,16 +236,15 @@ namespace Avalonia.Media.Imaging
/// Copies pixels to the target buffer and transcodes the pixel and alpha format if needed.
///
/// The target buffer.
- /// The alpha format.
///
- public void CopyPixels(ILockedFramebuffer buffer, AlphaFormat alphaFormat)
+ public void CopyPixels(ILockedFramebuffer buffer)
{
- if (PlatformImpl.Item is not IReadableBitmapWithAlphaImpl readable || readable.Format == null || readable.AlphaFormat == null)
+ if (PlatformImpl.Item is not IReadableBitmapImpl readable || readable.Format == null || readable.AlphaFormat == null)
{
throw new NotSupportedException("CopyPixels is not supported for this bitmap type");
}
- if (buffer.Format != readable.Format || alphaFormat != readable.AlphaFormat)
+ if (buffer.Format != readable.Format || buffer.AlphaFormat != readable.AlphaFormat)
{
using (var fb = readable.Lock())
{
@@ -255,11 +253,11 @@ namespace Avalonia.Media.Imaging
fb.Size,
fb.RowBytes,
fb.Format,
- readable.AlphaFormat.Value,
+ fb.AlphaFormat,
buffer.Address,
buffer.RowBytes,
buffer.Format,
- alphaFormat);
+ buffer.AlphaFormat);
}
}
else
diff --git a/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
index 435e51009d..f2b3b4d0c7 100644
--- a/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
+++ b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
@@ -71,10 +71,10 @@ namespace Avalonia.Media.Imaging
return new LockedFramebuffer(_pixelFormatMemory.Address, _pixelFormatMemory.Size,
_pixelFormatMemory.RowBytes,
- Dpi, _pixelFormatMemory.Format, () =>
+ Dpi, _pixelFormatMemory.Format, _pixelFormatMemory.AlphaFormat, () =>
{
using var inner = ((IWriteableBitmapImpl)PlatformImpl.Item).Lock();
- _pixelFormatMemory.CopyToRgba(Platform.AlphaFormat.Unpremul, inner.Address, inner.RowBytes);
+ _pixelFormatMemory.CopyToRgba(inner.AlphaFormat, inner.Address, inner.RowBytes);
});
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs b/src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs
index 0a8ab72a25..a747854e3b 100644
--- a/src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
+using System.Globalization;
namespace Avalonia.Media.TextFormatting
{
@@ -9,28 +7,17 @@ namespace Avalonia.Media.TextFormatting
///
public class GenericTextRunProperties : TextRunProperties
{
- private const double DefaultFontRenderingEmSize = 12;
+ internal const double DefaultFontRenderingEmSize = 12;
- // TODO12: Remove in 12.0.0 and make fontFeatures parameter in main ctor optional
- public GenericTextRunProperties(Typeface typeface, double fontRenderingEmSize = DefaultFontRenderingEmSize,
- TextDecorationCollection? textDecorations = null, IBrush? foregroundBrush = null,
- IBrush? backgroundBrush = null, BaselineAlignment baselineAlignment = BaselineAlignment.Baseline,
- CultureInfo? cultureInfo = null) :
- this(typeface, null, fontRenderingEmSize, textDecorations, foregroundBrush,
- backgroundBrush, baselineAlignment, cultureInfo)
- {
- }
-
- // TODO12:Change signature in 12.0.0
public GenericTextRunProperties(
- Typeface typeface,
- FontFeatureCollection? fontFeatures,
+ Typeface typeface,
double fontRenderingEmSize = DefaultFontRenderingEmSize,
TextDecorationCollection? textDecorations = null,
IBrush? foregroundBrush = null,
IBrush? backgroundBrush = null,
BaselineAlignment baselineAlignment = BaselineAlignment.Baseline,
- CultureInfo? cultureInfo = null)
+ CultureInfo? cultureInfo = null,
+ FontFeatureCollection? fontFeatures = null)
{
Typeface = typeface;
FontRenderingEmSize = fontRenderingEmSize;
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
index dcee8cea46..b621dada54 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
@@ -26,16 +26,14 @@
/// Text line to collapse.
public abstract TextRun[]? Collapse(TextLine textLine);
- // TODO12: Remove the flowDirection parameter
///
/// Creates a list of runs for given collapsed length which includes specified symbol at the end.
///
/// The text line.
/// The collapsed length.
- /// The flow direction.
/// The symbol.
/// List of remaining runs.
- public static TextRun[] CreateCollapsedRuns(TextLine textLine, int collapsedLength, FlowDirection flowDirection, TextRun shapedSymbol)
+ public static TextRun[] CreateCollapsedRuns(TextLine textLine, int collapsedLength, TextRun shapedSymbol)
{
if (collapsedLength <= 0)
{
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
index 426388f295..685bb08f27 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
@@ -71,7 +71,7 @@ namespace Avalonia.Media.TextFormatting
collapsedLength += measuredLength;
- return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, properties.FlowDirection, shapedSymbol);
+ return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, shapedSymbol);
}
availableWidth -= textRunWidth;
@@ -84,7 +84,7 @@ namespace Avalonia.Media.TextFormatting
//The whole run needs to fit into available space
if (drawableRun.Size.Width > availableWidth)
{
- return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, properties.FlowDirection, shapedSymbol);
+ return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, shapedSymbol);
}
availableWidth -= drawableRun.Size.Width;
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs
index 1dbf55fb97..ed60dd44a0 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs
@@ -59,8 +59,8 @@
var cultureInfo = textRun.Properties.CultureInfo;
- var shaperOptions = new TextShaperOptions(glyphTypeface, textRun.Properties.FontFeatures,
- fontRenderingEmSize, (sbyte)flowDirection, cultureInfo);
+ var shaperOptions = new TextShaperOptions(glyphTypeface, fontRenderingEmSize,
+ (sbyte)flowDirection, cultureInfo, 0, 0, textRun.Properties.FontFeatures);
var shapedBuffer = textShaper.ShapeText(textRun.Text, shaperOptions);
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
index 605804a82d..4428dff0f1 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
@@ -290,9 +290,13 @@ namespace Avalonia.Media.TextFormatting
}
var shaperOptions = new TextShaperOptions(
- properties.CachedGlyphTypeface, properties.FontFeatures,
- properties.FontRenderingEmSize, shapeableRun.BidiLevel, properties.CultureInfo,
- paragraphProperties.DefaultIncrementalTab, paragraphProperties.LetterSpacing);
+ properties.CachedGlyphTypeface,
+ properties.FontRenderingEmSize,
+ shapeableRun.BidiLevel,
+ properties.CultureInfo,
+ paragraphProperties.DefaultIncrementalTab,
+ paragraphProperties.LetterSpacing,
+ properties.FontFeatures);
ShapeTogether(groupedRuns, text, shaperOptions, textShaper, shapedRuns);
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
index 3a578fb72d..24e9019084 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
@@ -17,7 +17,6 @@ namespace Avalonia.Media.TextFormatting
private int _textSourceLength;
- // TODO12: Remove in 12.0.0 and make fontFeatures parameter in main ctor optional
///
/// Initializes a new instance of the class.
///
@@ -35,54 +34,13 @@ namespace Avalonia.Media.TextFormatting
/// The height of each line of text.
/// The letter spacing that is applied to rendered glyphs.
/// The maximum number of text lines.
- /// The text style overrides.
- public TextLayout(
- string? text,
- Typeface typeface,
- double fontSize,
- IBrush? foreground,
- TextAlignment textAlignment = TextAlignment.Left,
- TextWrapping textWrapping = TextWrapping.NoWrap,
- TextTrimming? textTrimming = null,
- TextDecorationCollection? textDecorations = null,
- FlowDirection flowDirection = FlowDirection.LeftToRight,
- double maxWidth = double.PositiveInfinity,
- double maxHeight = double.PositiveInfinity,
- double lineHeight = double.NaN,
- double letterSpacing = 0,
- int maxLines = 0,
- IReadOnlyList>? textStyleOverrides = null)
- : this(text, typeface, null, fontSize, foreground, textAlignment, textWrapping, textTrimming, textDecorations,
- flowDirection, maxWidth, maxHeight, lineHeight, letterSpacing, maxLines, textStyleOverrides)
- {
- }
-
- // TODO12:Change signature in 12.0.0
- ///
- /// Initializes a new instance of the class.
- ///
- /// The text.
- /// The typeface.
- /// Size of the font.
- /// The foreground.
- /// The text alignment.
- /// The text wrapping.
- /// The text trimming.
- /// The text decorations.
- /// The text flow direction.
- /// The maximum width.
- /// The maximum height.
- /// The height of each line of text.
- /// The letter spacing that is applied to rendered glyphs.
- /// The maximum number of text lines.
- /// The text style overrides.
/// Optional list of turned on/off features.
+ /// The text style overrides.
public TextLayout(
string? text,
Typeface typeface,
- FontFeatureCollection? fontFeatures,
- double fontSize,
- IBrush? foreground,
+ double fontSize = GenericTextRunProperties.DefaultFontRenderingEmSize,
+ IBrush? foreground = null,
TextAlignment textAlignment = TextAlignment.Left,
TextWrapping textWrapping = TextWrapping.NoWrap,
TextTrimming? textTrimming = null,
@@ -93,6 +51,7 @@ namespace Avalonia.Media.TextFormatting
double lineHeight = double.NaN,
double letterSpacing = 0,
int maxLines = 0,
+ FontFeatureCollection? fontFeatures = null,
IReadOnlyList>? textStyleOverrides = null)
{
_paragraphProperties =
@@ -534,7 +493,7 @@ namespace Avalonia.Media.TextFormatting
TextDecorationCollection? textDecorations, FlowDirection flowDirection, double lineHeight,
double letterSpacing, FontFeatureCollection? features)
{
- var textRunStyle = new GenericTextRunProperties(typeface, features, fontSize, textDecorations, foreground);
+ var textRunStyle = new GenericTextRunProperties(typeface, fontSize, textDecorations, foreground, fontFeatures: features);
return new GenericTextParagraphProperties(flowDirection, textAlignment, true, false,
textRunStyle, textWrapping, lineHeight, 0, letterSpacing);
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs
index cf2f74ee4d..ac4cf4855d 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs
@@ -107,8 +107,15 @@ namespace Avalonia.Media.TextFormatting
if (this is GenericTextRunProperties other && other.Typeface == typeface)
return this;
- return new GenericTextRunProperties(typeface, FontFeatures, FontRenderingEmSize,
- TextDecorations, ForegroundBrush, BackgroundBrush, BaselineAlignment);
+ return new GenericTextRunProperties(
+ typeface,
+ FontRenderingEmSize,
+ TextDecorations,
+ ForegroundBrush,
+ BackgroundBrush,
+ BaselineAlignment,
+ CultureInfo,
+ FontFeatures);
}
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
index 9ae20a3eaa..f19ce2ee5d 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
@@ -8,27 +8,14 @@ namespace Avalonia.Media.TextFormatting
///
public readonly record struct TextShaperOptions
{
- // TODO12: Remove in 12.0.0 and make fontFeatures parameter in main ctor optional
- public TextShaperOptions(
- GlyphTypeface typeface,
- double fontRenderingEmSize = 12,
- sbyte bidiLevel = 0,
- CultureInfo? culture = null,
- double incrementalTabWidth = 0,
- double letterSpacing = 0)
- : this(typeface, null, fontRenderingEmSize, bidiLevel, culture, incrementalTabWidth, letterSpacing)
- {
- }
-
- // TODO12:Change signature in 12.0.0
public TextShaperOptions(
GlyphTypeface typeface,
- IReadOnlyList? fontFeatures,
- double fontRenderingEmSize = 12,
+ double fontRenderingEmSize = GenericTextRunProperties.DefaultFontRenderingEmSize,
sbyte bidiLevel = 0,
CultureInfo? culture = null,
double incrementalTabWidth = 0,
- double letterSpacing = 0)
+ double letterSpacing = 0,
+ IReadOnlyList? fontFeatures = null)
{
GlyphTypeface = typeface;
FontRenderingEmSize = fontRenderingEmSize;
diff --git a/src/Avalonia.Base/Platform/ILockedFramebuffer.cs b/src/Avalonia.Base/Platform/ILockedFramebuffer.cs
index f963b77cd9..a73b339fcc 100644
--- a/src/Avalonia.Base/Platform/ILockedFramebuffer.cs
+++ b/src/Avalonia.Base/Platform/ILockedFramebuffer.cs
@@ -29,6 +29,9 @@ namespace Avalonia.Platform
///
PixelFormat Format { get; }
- //TODO12: Add AlphaFormat
+ ///
+ /// Gets the alpha format.
+ ///
+ AlphaFormat AlphaFormat { get; }
}
}
diff --git a/src/Avalonia.Base/Platform/IReadableBitmapImpl.cs b/src/Avalonia.Base/Platform/IReadableBitmapImpl.cs
index d5a0c765cc..6332d302af 100644
--- a/src/Avalonia.Base/Platform/IReadableBitmapImpl.cs
+++ b/src/Avalonia.Base/Platform/IReadableBitmapImpl.cs
@@ -2,15 +2,10 @@ using Avalonia.Metadata;
namespace Avalonia.Platform;
-public interface IReadableBitmapImpl
+[PrivateApi]
+public interface IReadableBitmapImpl : IBitmapImpl
{
PixelFormat? Format { get; }
- ILockedFramebuffer Lock();
-}
-
-//TODO12: Remove me once we can change IReadableBitmapImpl
-[Unstable]
-public interface IReadableBitmapWithAlphaImpl : IReadableBitmapImpl
-{
AlphaFormat? AlphaFormat { get; }
+ ILockedFramebuffer Lock();
}
diff --git a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
index 685491a326..185b116c9a 100644
--- a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
+++ b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
@@ -5,8 +5,8 @@ namespace Avalonia.Platform
///
/// Defines the platform-specific interface for a .
///
- [Unstable]
- public interface IWriteableBitmapImpl : IBitmapImpl, IReadableBitmapWithAlphaImpl
+ [PrivateApi]
+ public interface IWriteableBitmapImpl : IBitmapImpl, IReadableBitmapImpl
{
}
}
diff --git a/src/Avalonia.Base/Platform/LockedFramebuffer.cs b/src/Avalonia.Base/Platform/LockedFramebuffer.cs
index b9094d9e14..8924947662 100644
--- a/src/Avalonia.Base/Platform/LockedFramebuffer.cs
+++ b/src/Avalonia.Base/Platform/LockedFramebuffer.cs
@@ -7,7 +7,7 @@ namespace Avalonia.Platform
private readonly Action? _onDispose;
public LockedFramebuffer(IntPtr address, PixelSize size, int rowBytes, Vector dpi, PixelFormat format,
- Action? onDispose)
+ AlphaFormat alphaFormat, Action? onDispose)
{
_onDispose = onDispose;
Address = address;
@@ -15,6 +15,7 @@ namespace Avalonia.Platform
RowBytes = rowBytes;
Dpi = dpi;
Format = format;
+ AlphaFormat = alphaFormat;
}
public IntPtr Address { get; }
@@ -22,6 +23,7 @@ namespace Avalonia.Platform
public int RowBytes { get; }
public Vector Dpi { get; }
public PixelFormat Format { get; }
+ public AlphaFormat AlphaFormat { get; }
public void Dispose()
{
diff --git a/src/Avalonia.Base/Platform/RetainedFramebuffer.cs b/src/Avalonia.Base/Platform/RetainedFramebuffer.cs
index e5ca1070dc..e1983afde1 100644
--- a/src/Avalonia.Base/Platform/RetainedFramebuffer.cs
+++ b/src/Avalonia.Base/Platform/RetainedFramebuffer.cs
@@ -1,6 +1,4 @@
using System;
-using System.Runtime.InteropServices;
-using Avalonia.Metadata;
using Avalonia.Platform.Internal;
namespace Avalonia.Platform;
@@ -10,6 +8,7 @@ internal class RetainedFramebuffer : IDisposable
public PixelSize Size { get; }
public int RowBytes { get; }
public PixelFormat Format { get; }
+ public AlphaFormat AlphaFormat { get; }
public IntPtr Address => _blob?.Address ?? throw new ObjectDisposedException(nameof(RetainedFramebuffer));
private UnmanagedBlob? _blob;
@@ -17,13 +16,13 @@ internal class RetainedFramebuffer : IDisposable
? format
: throw new ArgumentOutOfRangeException(nameof(format));
- public RetainedFramebuffer(PixelSize size, PixelFormat format) : this(size, ValidateKnownFormat(format),
- format.BitsPerPixel / 8 * size.Width)
+ public RetainedFramebuffer(PixelSize size, PixelFormat format, AlphaFormat alphaFormat)
+ : this(size, ValidateKnownFormat(format), alphaFormat, format.BitsPerPixel / 8 * size.Width)
{
}
- public RetainedFramebuffer(PixelSize size, PixelFormat format, int rowBytes)
+ public RetainedFramebuffer(PixelSize size, PixelFormat format, AlphaFormat alphaFormat, int rowBytes)
{
if (size.Width <= 0 || size.Height <= 0)
throw new ArgumentOutOfRangeException(nameof(size));
@@ -32,6 +31,7 @@ internal class RetainedFramebuffer : IDisposable
Size = size;
RowBytes = rowBytes;
Format = format;
+ AlphaFormat = alphaFormat;
_blob = new UnmanagedBlob(RowBytes * size.Height);
}
@@ -39,7 +39,7 @@ internal class RetainedFramebuffer : IDisposable
{
if (_blob == null)
throw new ObjectDisposedException(nameof(RetainedFramebuffer));
- return new LockedFramebuffer(_blob.Address, Size, RowBytes, dpi, Format, () =>
+ return new LockedFramebuffer(_blob.Address, Size, RowBytes, dpi, Format, AlphaFormat, () =>
{
blit(this);
GC.KeepAlive(this);
@@ -51,4 +51,4 @@ internal class RetainedFramebuffer : IDisposable
_blob?.Dispose();
_blob = null;
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Base/Utilities/StringTokenizer.cs b/src/Avalonia.Base/Utilities/StringTokenizer.cs
deleted file mode 100644
index e83ef8c479..0000000000
--- a/src/Avalonia.Base/Utilities/StringTokenizer.cs
+++ /dev/null
@@ -1,245 +0,0 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-using static System.Char;
-
-namespace Avalonia.Utilities
-{
- // TODO12: Remove this struct in 12.0 (breaking change)
-
- [Obsolete("This type has been superseded by SpanStringTokenizer.")]
-#if !BUILDTASK
- public
-#endif
- record struct StringTokenizer : IDisposable
- {
- private const char DefaultSeparatorChar = ',';
-
- private readonly string _s;
- private readonly int _length;
- private readonly char _separator;
- private readonly string? _exceptionMessage;
- private readonly IFormatProvider _formatProvider;
- private int _index;
- private int _tokenIndex;
- private int _tokenLength;
-
- public StringTokenizer(string s, IFormatProvider formatProvider, string? exceptionMessage = null)
- : this(s, GetSeparatorFromFormatProvider(formatProvider), exceptionMessage)
- {
- _formatProvider = formatProvider;
- }
-
- public StringTokenizer(string s, char separator = DefaultSeparatorChar, string? exceptionMessage = null)
- {
- _s = s ?? throw new ArgumentNullException(nameof(s));
- _length = s?.Length ?? 0;
- _separator = separator;
- _exceptionMessage = exceptionMessage;
- _formatProvider = CultureInfo.InvariantCulture;
- _index = 0;
- _tokenIndex = -1;
- _tokenLength = 0;
-
- while (_index < _length && IsWhiteSpace(_s, _index))
- {
- _index++;
- }
- }
-
- public string? CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength);
-
- public ReadOnlySpan CurrentTokenSpan => _tokenIndex < 0 ? ReadOnlySpan.Empty : _s.AsSpan().Slice(_tokenIndex, _tokenLength);
-
- public void Dispose()
- {
- if (_index != _length)
- {
- throw GetFormatException();
- }
- }
-
- public bool TryReadInt32(out Int32 result, char? separator = null)
- {
- if (TryReadSpan(out var stringResult, separator) &&
- SpanHelpers.TryParseInt(stringResult, NumberStyles.Integer, _formatProvider, out result))
- {
- return true;
- }
- else
- {
- result = default;
- return false;
- }
- }
-
- public int ReadInt32(char? separator = null)
- {
- if (!TryReadInt32(out var result, separator))
- {
- throw GetFormatException();
- }
-
- return result;
- }
-
- public bool TryReadDouble(out double result, char? separator = null)
- {
- if (TryReadSpan(out var stringResult, separator) &&
- SpanHelpers.TryParseDouble(stringResult, NumberStyles.Float, _formatProvider, out result))
- {
- return true;
- }
- else
- {
- result = default;
- return false;
- }
- }
-
- public double ReadDouble(char? separator = null)
- {
- if (!TryReadDouble(out var result, separator))
- {
- throw GetFormatException();
- }
-
- return result;
- }
-
- public bool TryReadString([NotNull] out string result, char? separator = null)
- {
- var success = TryReadToken(separator ?? _separator);
- result = CurrentTokenSpan.ToString();
- return success;
- }
-
- public string ReadString(char? separator = null)
- {
- if (!TryReadString(out var result, separator))
- {
- throw GetFormatException();
- }
-
- return result;
- }
-
- public bool TryReadSpan(out ReadOnlySpan result, char? separator = null)
- {
- var success = TryReadToken(separator ?? _separator);
- result = CurrentTokenSpan;
- return success;
- }
-
- public ReadOnlySpan ReadSpan(char? separator = null)
- {
- if (!TryReadSpan(out var result, separator))
- {
- throw GetFormatException();
- }
-
- return result;
- }
-
- private bool TryReadToken(char separator)
- {
- _tokenIndex = -1;
-
- if (_index >= _length)
- {
- return false;
- }
-
- var c = _s[_index];
-
- var index = _index;
- var length = 0;
-
- while (_index < _length)
- {
- c = _s[_index];
-
- if (IsWhiteSpace(c) || c == separator)
- {
- break;
- }
-
- _index++;
- length++;
- }
-
- SkipToNextToken(separator);
-
- _tokenIndex = index;
- _tokenLength = length;
-
- if (_tokenLength < 1)
- {
- throw GetFormatException();
- }
-
- return true;
- }
-
- private void SkipToNextToken(char separator)
- {
- if (_index < _length)
- {
- var c = _s[_index];
-
- if (c != separator && !IsWhiteSpace(c))
- {
- throw GetFormatException();
- }
-
- var length = 0;
-
- while (_index < _length)
- {
- c = _s[_index];
-
- if (c == separator)
- {
- length++;
- _index++;
-
- if (length > 1)
- {
- throw GetFormatException();
- }
- }
- else
- {
- if (!IsWhiteSpace(c))
- {
- break;
- }
-
- _index++;
- }
- }
-
- if (length > 0 && _index >= _length)
- {
- throw GetFormatException();
- }
- }
- }
-
- private FormatException GetFormatException() =>
- _exceptionMessage != null ? new FormatException(_exceptionMessage) : new FormatException();
-
- private static char GetSeparatorFromFormatProvider(IFormatProvider provider)
- {
- var c = DefaultSeparatorChar;
-
- var formatInfo = NumberFormatInfo.GetInstance(provider);
- if (formatInfo.NumberDecimalSeparator.Length > 0 && c == formatInfo.NumberDecimalSeparator[0])
- {
- c = ';';
- }
-
- return c;
- }
- }
-}
diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
index 313e66b207..bc6380a012 100644
--- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
+++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
@@ -62,9 +62,6 @@
Markup/%(RecursiveDir)%(FileName)%(Extension)
-
- Markup/%(RecursiveDir)%(FileName)%(Extension)
-
Markup/%(RecursiveDir)%(FileName)%(Extension)
diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
index 138f6d3b90..305000a0b1 100644
--- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
+++ b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
@@ -32,7 +32,7 @@ namespace Avalonia.Build.Tasks
ProjectDirectory, VerifyIl, DefaultCompileBindings, outputImportance,
new XamlCompilerDiagnosticsFilter(AnalyzerConfigFiles),
(SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null,
- SkipXamlCompilation, DebuggerLaunch, VerboseExceptions);
+ SkipXamlCompilation, DebuggerLaunch, VerboseExceptions, CreateSourceInfo);
if (res.Success && !res.WrittenFile)
{
@@ -99,6 +99,8 @@ namespace Avalonia.Build.Tasks
public bool DebuggerLaunch { get; set; }
+ public bool CreateSourceInfo { get; set; }
+
public bool VerboseExceptions { get; set; }
public ITaskItem[] AnalyzerConfigFiles { get; set; }
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index 50b0fea6b3..db4b6a7f6f 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -52,7 +52,7 @@ namespace Avalonia.Build.Tasks
string[] references, string projectDirectory,
bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance,
XamlCompilerDiagnosticsFilter diagnosticsFilter, string strongNameKey,
- bool skipXamlCompilation, bool debuggerLaunch, bool verboseExceptions)
+ bool skipXamlCompilation, bool debuggerLaunch, bool verboseExceptions, bool createSourceInfo)
{
try
{
@@ -67,7 +67,7 @@ namespace Avalonia.Build.Tasks
var compileRes = CompileCore(
engine, typeSystem, projectDirectory, verifyIl,
defaultCompileBindings, logImportance, diagnosticsFilter,
- debuggerLaunch, verboseExceptions);
+ debuggerLaunch, verboseExceptions, createSourceInfo);
if (compileRes == null)
return new CompileResult(true);
if (compileRes == false)
@@ -107,7 +107,8 @@ namespace Avalonia.Build.Tasks
MessageImportance logImportance,
XamlCompilerDiagnosticsFilter diagnosticsFilter,
bool debuggerLaunch,
- bool verboseExceptions)
+ bool verboseExceptions,
+ bool createSourceInfo)
{
if (debuggerLaunch)
{
@@ -210,6 +211,7 @@ namespace Avalonia.Build.Tasks
{
EnableIlVerification = verifyIl,
DefaultCompileBindings = defaultCompileBindings,
+ CreateSourceInfo = createSourceInfo,
DynamicSetterContainerProvider = new DefaultXamlDynamicSetterContainerProvider(dynamicSettersBuilder)
};
diff --git a/src/Avalonia.Controls/Design.cs b/src/Avalonia.Controls/Design.cs
index 9d6bb93ebb..1e7912d75f 100644
--- a/src/Avalonia.Controls/Design.cs
+++ b/src/Avalonia.Controls/Design.cs
@@ -127,22 +127,6 @@ namespace Avalonia.Controls
///
public static readonly AttachedProperty PreviewWithProperty = AvaloniaProperty
.RegisterAttached("PreviewWith", typeof (Design));
-
- ///
- /// Sets a preview template for the specified at design-time.
- ///
- ///
- /// This method allows you to specify a substitute control to be rendered in the previewer
- /// for a given object.
- ///
- /// The target object.
- /// The preview control.
- // TODO12: Remove this overload in Avalonia 12
- [Obsolete("Use SetPreviewWith(AvaloniaObject, ITemplate) overload instead. Use from XAML")]
- public static void SetPreviewWith(AvaloniaObject target, Control? control)
- {
- s_previewWith[target] = control is not null ? new FuncTemplate(() => control) : null;
- }
///
/// Sets a preview template for the specified at design-time.
diff --git a/src/Avalonia.Controls/Documents/Inline.cs b/src/Avalonia.Controls/Documents/Inline.cs
index 1ededcf81c..4338dc6d53 100644
--- a/src/Avalonia.Controls/Documents/Inline.cs
+++ b/src/Avalonia.Controls/Documents/Inline.cs
@@ -10,11 +10,10 @@ namespace Avalonia.Controls.Documents
///
public abstract class Inline : TextElement
{
- // TODO12: change the field type to an AttachedProperty for consistency (breaking change)
///
/// AvaloniaProperty for property.
///
- public static readonly StyledProperty TextDecorationsProperty =
+ public static readonly AttachedProperty TextDecorationsProperty =
AvaloniaProperty.RegisterAttached(
nameof(TextDecorations),
inherits: true);
@@ -82,12 +81,13 @@ namespace Avalonia.Controls.Documents
return new GenericTextRunProperties(
typeface,
- FontFeatures,
FontSize,
- TextDecorations,
+ TextDecorations,
Foreground,
parentOrSelfBackground,
- BaselineAlignment);
+ BaselineAlignment,
+ null,
+ FontFeatures);
}
///
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 7078650da4..05c74b39e5 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -346,13 +346,26 @@ namespace Avalonia.Controls.Presenters
private TextLayout CreateTextLayoutInternal(Size constraint, string? text, Typeface typeface,
IReadOnlyList>? textStyleOverrides)
{
- var foreground = Foreground;
var maxWidth = MathUtilities.IsZero(constraint.Width) ? double.PositiveInfinity : constraint.Width;
var maxHeight = MathUtilities.IsZero(constraint.Height) ? double.PositiveInfinity : constraint.Height;
- var textLayout = new TextLayout(text, typeface, FontFeatures, FontSize, foreground, TextAlignment,
- TextWrapping, maxWidth: maxWidth, maxHeight: maxHeight, textStyleOverrides: textStyleOverrides,
- flowDirection: FlowDirection, lineHeight: LineHeight, letterSpacing: LetterSpacing);
+ var textLayout = new TextLayout(
+ text,
+ typeface,
+ FontSize,
+ Foreground,
+ TextAlignment,
+ TextWrapping,
+ null,
+ null,
+ FlowDirection,
+ maxWidth,
+ maxHeight,
+ LineHeight,
+ LetterSpacing,
+ 0,
+ FontFeatures,
+ textStyleOverrides);
return textLayout;
}
@@ -553,9 +566,12 @@ namespace Avalonia.Controls.Presenters
if (!string.IsNullOrEmpty(preeditText))
{
var preeditHighlight = new ValueSpan(caretIndex, preeditText.Length,
- new GenericTextRunProperties(typeface, FontFeatures, FontSize,
- foregroundBrush: foreground,
- textDecorations: TextDecorations.Underline));
+ new GenericTextRunProperties(
+ typeface,
+ FontSize,
+ TextDecorations.Underline,
+ foreground,
+ fontFeatures: FontFeatures));
textStyleOverrides = new[]
{
@@ -569,8 +585,11 @@ namespace Avalonia.Controls.Presenters
textStyleOverrides = new[]
{
new ValueSpan(start, length,
- new GenericTextRunProperties(typeface, FontFeatures, FontSize,
- foregroundBrush: SelectionForegroundBrush))
+ new GenericTextRunProperties(
+ typeface,
+ FontSize,
+ foregroundBrush: SelectionForegroundBrush,
+ fontFeatures: FontFeatures))
};
}
}
diff --git a/src/Avalonia.Controls/Primitives/TextSearch.cs b/src/Avalonia.Controls/Primitives/TextSearch.cs
index 5099567630..aa83266683 100644
--- a/src/Avalonia.Controls/Primitives/TextSearch.cs
+++ b/src/Avalonia.Controls/Primitives/TextSearch.cs
@@ -24,22 +24,20 @@ namespace Avalonia.Controls.Primitives
public static readonly AttachedProperty TextBindingProperty
= AvaloniaProperty.RegisterAttached("TextBinding", typeof(TextSearch));
- // TODO12: Control should be Interactive to match the property definition.
///
/// Sets the value of the attached property to a given .
///
/// The control.
/// The search text to set.
- public static void SetText(Control control, string? text)
+ public static void SetText(Interactive control, string? text)
=> control.SetValue(TextProperty, text);
- // TODO12: Control should be Interactive to match the property definition.
///
/// Gets the value of the attached property from a given .
///
/// The control.
/// The search text.
- public static string? GetText(Control control)
+ public static string? GetText(Interactive control)
=> control.GetValue(TextProperty);
///
diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs
index 593adfb225..cc7d7608ff 100644
--- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs
+++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs
@@ -76,6 +76,7 @@ namespace Avalonia.Controls.Remote.Server
Stride,
new Vector(_dpi, _dpi),
new PlatformPixelFormat((PixelFormatEnum)Format),
+ Format == ProtocolPixelFormat.Rgb565 ? AlphaFormat.Opaque : AlphaFormat.Premul,
() =>
{
handle.Free();
diff --git a/src/Avalonia.Controls/SelectableTextBlock.cs b/src/Avalonia.Controls/SelectableTextBlock.cs
index 55321655cc..f3532763f6 100644
--- a/src/Avalonia.Controls/SelectableTextBlock.cs
+++ b/src/Avalonia.Controls/SelectableTextBlock.cs
@@ -186,10 +186,10 @@ namespace Avalonia.Controls
var defaultProperties = new GenericTextRunProperties(
typeface,
- FontFeatures,
FontSize,
TextDecorations,
- Foreground);
+ Foreground,
+ fontFeatures: FontFeatures);
var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, TextAlignment, true, false,
defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing)
@@ -235,9 +235,9 @@ namespace Avalonia.Controls
overlapLength,
new GenericTextRunProperties(
textRun.Properties?.Typeface ?? typeface,
- textRun.Properties?.FontFeatures ?? FontFeatures,
FontSize,
- foregroundBrush: SelectionForegroundBrush)));
+ foregroundBrush: SelectionForegroundBrush,
+ fontFeatures: textRun.Properties?.FontFeatures ?? FontFeatures)));
accumulatedLength += runLength;
}
@@ -247,8 +247,11 @@ namespace Avalonia.Controls
textStyleOverrides =
[
new ValueSpan(start, length,
- new GenericTextRunProperties(typeface, FontFeatures, FontSize,
- foregroundBrush: SelectionForegroundBrush))
+ new GenericTextRunProperties(
+ typeface,
+ FontSize,
+ foregroundBrush: SelectionForegroundBrush,
+ fontFeatures: FontFeatures))
];
}
}
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index 55cbae773c..c70d06ae7f 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -657,10 +657,10 @@ namespace Avalonia.Controls
var defaultProperties = new GenericTextRunProperties(
typeface,
- FontFeatures,
FontSize,
TextDecorations,
- Foreground);
+ Foreground,
+ fontFeatures: FontFeatures);
var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, IsMeasureValid ? TextAlignment : TextAlignment.Left, true, false,
defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing)
diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs
index fd3f3ce676..a352fc2e8b 100644
--- a/src/Avalonia.Native/DeferredFramebuffer.cs
+++ b/src/Avalonia.Native/DeferredFramebuffer.cs
@@ -29,6 +29,7 @@ namespace Avalonia.Native
public int RowBytes { get; set; }
public Vector Dpi { get; set; }
public PixelFormat Format { get; set; }
+ public AlphaFormat AlphaFormat { get; set; }
public void Dispose()
{
diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs
index 2af06960bc..c6be968bfd 100644
--- a/src/Avalonia.X11/X11CursorFactory.cs
+++ b/src/Avalonia.X11/X11CursorFactory.cs
@@ -133,7 +133,7 @@ namespace Avalonia.X11
return new LockedFramebuffer(
_blob.Address + Marshal.SizeOf(),
_pixelSize, _pixelSize.Width * 4,
- new Vector(96, 96), PixelFormat.Bgra8888, null);
+ new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul, null);
}
public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock);
diff --git a/src/Avalonia.X11/X11FramebufferSurface.cs b/src/Avalonia.X11/X11FramebufferSurface.cs
index 5b01ce91de..4efd7f072d 100644
--- a/src/Avalonia.X11/X11FramebufferSurface.cs
+++ b/src/Avalonia.X11/X11FramebufferSurface.cs
@@ -61,7 +61,7 @@ namespace Avalonia.X11
{
_fb?.Dispose();
_fb = null;
- _fb = new RetainedFramebuffer(new PixelSize(width, height), PixelFormat.Bgra8888);
+ _fb = new RetainedFramebuffer(new PixelSize(width, height), PixelFormat.Bgra8888, AlphaFormat.Premul);
}
properties = new FramebufferLockProperties(framebufferValid);
diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs
index bde6de6614..1cfe009062 100644
--- a/src/Avalonia.X11/X11IconLoader.cs
+++ b/src/Avalonia.X11/X11IconLoader.cs
@@ -83,7 +83,7 @@ namespace Avalonia.X11
{
var h = GCHandle.Alloc(_bdata, GCHandleType.Pinned);
return new LockedFramebuffer(h.AddrOfPinnedObject(), new PixelSize(_width, _height), _width * 4,
- new Vector(96, 96), PixelFormat.Bgra8888,
+ new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul,
() => h.Free());
}
diff --git a/src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs b/src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs
index b3395e87d9..6a0fb61f70 100644
--- a/src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs
+++ b/src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs
@@ -47,7 +47,7 @@ partial class BrowserSoftwareRenderTarget : BrowserRenderTarget, IFramebufferPla
{
_fb?.Dispose();
_fb = null;
- _fb = new RetainedFramebuffer(size, PixelFormat.Rgba8888);
+ _fb = new RetainedFramebuffer(size, PixelFormat.Rgba8888, AlphaFormat.Premul);
}
return _fb.Lock(new Vector(scaling * 96, scaling * 96), _parent._blit);
@@ -66,4 +66,4 @@ partial class BrowserSoftwareRenderTarget : BrowserRenderTarget, IFramebufferPla
{
PutPixelData(Js, fb.Address.ToInt32(), fb.Size.Width * fb.Size.Height * 4, fb.Size.Width, fb.Size.Height);
}
-}
\ No newline at end of file
+}
diff --git a/src/Browser/Avalonia.Browser/Rendering/RenderWorker.cs b/src/Browser/Avalonia.Browser/Rendering/RenderWorker.cs
index f854e922f8..91495ea0ea 100644
--- a/src/Browser/Avalonia.Browser/Rendering/RenderWorker.cs
+++ b/src/Browser/Avalonia.Browser/Rendering/RenderWorker.cs
@@ -1,10 +1,7 @@
using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.JavaScript;
-using System.Threading;
using System.Threading.Tasks;
using Avalonia.Browser.Interop;
@@ -19,11 +16,14 @@ internal partial class RenderWorker
private static partial void InitializeRenderTargets();
internal static int WorkerThreadId;
+
+ // The worker task needs to be rooted otherwise the web worker will exit.
+ private static Task? s_workerTask;
public static Task InitializeAsync()
{
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- var workerTask = JSWebWorkerClone.RunAsync(async () =>
+ s_workerTask = JSWebWorkerRunAsync(null, async () =>
{
try
{
@@ -41,103 +41,18 @@ internal partial class RenderWorker
}
});
- workerTask.ContinueWith(_ =>
+ s_workerTask.ContinueWith(_ =>
{
- if (workerTask.IsFaulted)
- tcs.TrySetException(workerTask.Exception);
+ if (s_workerTask.IsFaulted)
+ tcs.TrySetException(s_workerTask.Exception);
});
return tcs.Task;
}
- public static class JSWebWorkerClone
- {
- private static readonly MethodInfo _setExtLoop;
- private static readonly MethodInfo _intallInterop;
-
- [DynamicDependency(DynamicallyAccessedMemberTypes.All, "System.Runtime.InteropServices.JavaScript.JSSynchronizationContext",
- "System.Runtime.InteropServices.JavaScript")]
- [DynamicDependency(DynamicallyAccessedMemberTypes.All, "System.Runtime.InteropServices.JavaScript.JSHostImplementation",
- "System.Runtime.InteropServices.JavaScript")]
- [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Private runtime API")]
- [UnconditionalSuppressMessage("Trimming", "IL2036", Justification = "Private runtime API")]
- [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Private runtime API")]
- [UnconditionalSuppressMessage("Trimming", "IL2111", Justification = "Private runtime API")]
- static JSWebWorkerClone()
- {
- var syncContext = typeof(System.Runtime.InteropServices.JavaScript.JSHost)
- .Assembly!.GetType("System.Runtime.InteropServices.JavaScript.JSSynchronizationContext")!;
- var hostImpl = typeof(System.Runtime.InteropServices.JavaScript.JSHost)
- .Assembly!.GetType("System.Runtime.InteropServices.JavaScript.JSHostImplementation")!;
-
- _setExtLoop = hostImpl.GetMethod("SetHasExternalEventLoop")!;
- _intallInterop = syncContext.GetMethod("InstallWebWorkerInterop")!;
- }
-
- public static Task RunAsync(Func run)
- {
- var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- var th = new Thread(_ =>
- {
- _intallInterop.Invoke(null, [false, CancellationToken.None]);
- try
- {
- run().ContinueWith(t =>
- {
- if (t.IsFaulted)
- tcs.TrySetException(t.Exception);
- else if (t.IsCanceled)
- tcs.TrySetCanceled();
- else
- tcs.TrySetResult();
- });
- }
- catch(Exception e)
- {
- tcs.TrySetException(e);
- }
- })
- {
- Name = "Manual JS worker"
- };
- _setExtLoop.Invoke(null, [th]);
-#pragma warning disable CA1416
- th.Start();
-#pragma warning restore CA1416
- return tcs.Task;
- }
-
- }
-
- // TODO: Use this class instead of JSWebWorkerClone once https://github.com/dotnet/runtime/issues/102010 is fixed
- // TODO12: It was fixed in .NET 10
- class JSWebWorkerWrapper
- {
- [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.JSWebWorker",
- "System.Runtime.InteropServices.JavaScript")]
- [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Private runtime API")]
- [UnconditionalSuppressMessage("Trimming", "IL2036", Justification = "Private runtime API")]
- [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Private runtime API")]
- [UnconditionalSuppressMessage("Trimming", "IL2111", Justification = "Private runtime API")]
- static JSWebWorkerWrapper()
- {
- var type = typeof(System.Runtime.InteropServices.JavaScript.JSHost)
- .Assembly!.GetType("System.Runtime.InteropServices.JavaScript.JSWebWorker");
-#pragma warning disable IL2075
- var m = type!
-
- .GetMethods(BindingFlags.Static | BindingFlags.Public
- ).First(m => m.Name == "RunAsync"
- && m.ReturnType == typeof(Task)
- && m.GetParameters() is { } parameters
- && parameters.Length == 1
- && parameters[0].ParameterType == typeof(Func));
-
-#pragma warning restore IL2075
- RunAsync = (Func, Task>) Delegate.CreateDelegate(typeof(Func, Task>), m);
-
- }
-
- public static Func, Task> RunAsync { get; set; }
- }
+ // Even though this API is public in the .NET code, it's not part of ref assemblies and is not a stable API.
+ [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "RunAsync")]
+ private static extern Task JSWebWorkerRunAsync(
+ [UnsafeAccessorType("System.Runtime.InteropServices.JavaScript.JSWebWorker, System.Runtime.InteropServices.JavaScript")] object? instance,
+ Func body);
}
diff --git a/src/Headless/Avalonia.Headless/Avalonia.Headless.csproj b/src/Headless/Avalonia.Headless/Avalonia.Headless.csproj
index bbd10677e9..edd5ed183b 100644
--- a/src/Headless/Avalonia.Headless/Avalonia.Headless.csproj
+++ b/src/Headless/Avalonia.Headless/Avalonia.Headless.csproj
@@ -6,6 +6,7 @@
+
diff --git a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index 0a29b9a5b8..05d835c57c 100644
--- a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
@@ -414,8 +414,8 @@ namespace Avalonia.Headless
public Vector Dpi { get; }
public PixelSize PixelSize { get; }
- public PixelFormat? Format { get; }
- public AlphaFormat? AlphaFormat { get; }
+ public PixelFormat? Format => PixelFormat.Rgba8888;
+ public AlphaFormat? AlphaFormat => Platform.AlphaFormat.Premul;
public int Version { get; set; }
public void Save(string fileName, int? quality = null)
@@ -434,7 +434,7 @@ namespace Avalonia.Headless
Version++;
var mem = Marshal.AllocHGlobal(PixelSize.Width * PixelSize.Height * 4);
return new LockedFramebuffer(mem, PixelSize, PixelSize.Width * 4, Dpi, PixelFormat.Rgba8888,
- () => Marshal.FreeHGlobal(mem));
+ Platform.AlphaFormat.Premul, () => Marshal.FreeHGlobal(mem));
}
}
diff --git a/src/Headless/Avalonia.Headless/HeadlessUnitTestSession.cs b/src/Headless/Avalonia.Headless/HeadlessUnitTestSession.cs
index 26c076c1e1..0e5a6b0c8b 100644
--- a/src/Headless/Avalonia.Headless/HeadlessUnitTestSession.cs
+++ b/src/Headless/Avalonia.Headless/HeadlessUnitTestSession.cs
@@ -236,7 +236,9 @@ public sealed class HeadlessUnitTestSession : IDisposable, IAsyncDisposable
// If windowing subsystem wasn't initialized by user, force headless with default parameters.
if (appBuilder.WindowingSubsystemName != "Headless")
{
- appBuilder = appBuilder.UseHeadless(new AvaloniaHeadlessPlatformOptions());
+ appBuilder = appBuilder
+ .UseHeadless(new AvaloniaHeadlessPlatformOptions())
+ .UseHarfBuzz();
}
// ReSharper disable once AccessToModifiedClosure
diff --git a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
index e4138bee66..8f80e22138 100644
--- a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
+++ b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
@@ -200,6 +200,7 @@ namespace Avalonia.Headless
public int RowBytes => _fb.RowBytes;
public Vector Dpi => _fb.Dpi;
public PixelFormat Format => _fb.Format;
+ public AlphaFormat AlphaFormat => _fb.AlphaFormat;
}
public ILockedFramebuffer Lock()
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
index 97b730c160..f9df24ed69 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
@@ -101,12 +101,17 @@ namespace Avalonia.LinuxFramebuffer.Output
public static LockedFramebuffer LockFb(IntPtr address, fb_var_screeninfo varInfo,
fb_fix_screeninfo fixedInfo, Vector dpi, Action? dispose)
{
+ var (format, alphaFormat) = varInfo switch
+ {
+ { bits_per_pixel: 16 } => (PixelFormat.Rgb565, AlphaFormat.Opaque),
+ { bits_per_pixel: 32, blue.offset: 16 } => (PixelFormat.Rgba8888, AlphaFormat.Premul),
+ _ => (PixelFormat.Bgra8888, AlphaFormat.Premul)
+ };
+
return new LockedFramebuffer(address,
new PixelSize((int)varInfo.xres, (int)varInfo.yres),
(int)fixedInfo.line_length, dpi,
- varInfo.bits_per_pixel == 16 ? PixelFormat.Rgb565
- : varInfo.blue.offset == 16 ? PixelFormat.Rgba8888
- : PixelFormat.Bgra8888, dispose);
+ format, alphaFormat, dispose);
}
private void BlitToDevice()
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
index 5ab4489c21..728fdb397a 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
@@ -344,7 +344,8 @@ namespace Avalonia.Markup.Xaml.XamlIl
{
EnableIlVerification = true,
DefaultCompileBindings = configuration.UseCompiledBindingsByDefault,
- IsDesignMode = configuration.DesignMode
+ IsDesignMode = configuration.DesignMode,
+ CreateSourceInfo = configuration.CreateSourceInfo,
};
var parsedDocuments = new List();
@@ -363,7 +364,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
}
var parsed = compiler.Parse(xaml, overrideType);
- parsed.Document = "runtimexaml:" + parsedDocuments.Count;
+ parsed.Document = document.Document ?? ("runtimexaml" + parsedDocuments.Count);
compiler.Transform(parsed);
var xamlName = GetSafeUriIdentifier(document.BaseUri)
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
index e0225a24f7..5b00ea0bea 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using Avalonia.Markup.Xaml.Loader.CompilerExtensions.Transformers;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX;
@@ -20,6 +21,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
private readonly IXamlType _contextType = null!;
private readonly AvaloniaXamlIlDesignPropertiesTransformer _designTransformer;
private readonly AvaloniaBindingExtensionTransformer _bindingTransformer;
+ private readonly AvaloniaXamlIlAddSourceInfoTransformer _addSourceInfoTransformer;
+ private readonly AvaloniaXamlResourceTransformer _resourceTransformer;
private AvaloniaXamlIlCompiler(TransformerConfiguration configuration, XamlLanguageEmitMappings emitMappings)
: base(configuration, emitMappings, true)
@@ -47,6 +50,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlResolveClassesPropertiesTransformer(),
new AvaloniaXamlIlTransformInstanceAttachedProperties(),
new AvaloniaXamlIlTransformSyntheticCompiledBindingMembers());
+
+
InsertAfter(
new AvaloniaXamlIlAvaloniaPropertyResolver(),
new AvaloniaXamlIlReorderClassesPropertiesTransformer(),
@@ -85,7 +90,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
);
InsertBeforeMany(new [] { typeof(DeferredContentTransformer), typeof(AvaloniaXamlIlCompiledBindingsMetadataRemover) },
- new AvaloniaXamlIlDeferredResourceTransformer());
+ _resourceTransformer = new AvaloniaXamlResourceTransformer());
InsertBefore(new AvaloniaXamlIlTransformRoutedEvent());
@@ -94,6 +99,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
Transformers.Add(new AvaloniaXamlIlEnsureResourceDictionaryCapacityTransformer());
Transformers.Add(new AvaloniaXamlIlRootObjectScope());
+ Transformers.Add(_addSourceInfoTransformer = new AvaloniaXamlIlAddSourceInfoTransformer());
+
Emitters.Add(new AvaloniaNameScopeRegistrationXamlIlNodeEmitter());
Emitters.Add(new AvaloniaXamlIlRootObjectScope.Emitter());
@@ -122,6 +129,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public const string PopulateName = "__AvaloniaXamlIlPopulate";
public const string BuildName = "__AvaloniaXamlIlBuild";
+ public bool CreateSourceInfo
+ {
+ get => _addSourceInfoTransformer.CreateSourceInfo || _resourceTransformer.CreateSourceInfo;
+ set => _addSourceInfoTransformer.CreateSourceInfo = _resourceTransformer.CreateSourceInfo = value;
+ }
+
public bool IsDesignMode
{
get => _designTransformer.IsDesignMode;
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
index 766c6d9e02..9ee893e0d4 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
@@ -20,8 +20,12 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
{
public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node)
{
- if (node is not XamlValueWithManipulationNode valueNode
- || valueNode.Value is not XamlAstNewClrObjectNode objectNode
+ // Filter object initialization nodes like:
+ // > XamlValueWithManipulationNode
+ // > > XamlAstNewClrObjectNode // StyleInclude or ResourceInclude, can be nested in another XamlValueWithManipulationNode
+ // > > XamlObjectInitializationNode
+ if (node is not XamlValueWithManipulationNode { Manipulation: XamlObjectInitializationNode initializationNode } valueNode
+ || valueNode.UnwrapValue() is not { } objectNode
|| (objectNode.Type.GetClrType() != context.GetAvaloniaTypes().StyleInclude
&& objectNode.Type.GetClrType() != context.GetAvaloniaTypes().ResourceInclude))
{
@@ -36,11 +40,6 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
throw new InvalidOperationException($"\"{nodeTypeName}\".Loaded property is expected to be defined");
}
- if (valueNode.Manipulation is not XamlObjectInitializationNode initializationNode)
- {
- throw new InvalidOperationException($"Invalid \"{nodeTypeName}\" node initialization.");
- }
-
var additionalProperties = new List();
if (initializationNode.Manipulation is not XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty)
{
@@ -176,9 +175,25 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
strictSourceValueType ? XamlDiagnosticSeverity.Error : XamlDiagnosticSeverity.Warning,
$"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri. This {nodeTypeName} will be resolved in runtime instead.",
node);
-
+
// We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`.
- if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceUriNode
+ if (sourceProperty.Values.Count != 1)
+ {
+ OnInvalidSource(sourceProperty);
+ return (null, null);
+ }
+
+ // `new Uri` can be wrapped in manipulation node if source info or another manipulation was applied.
+ var sourceUriNodeWrapped = sourceProperty.Values.Single();
+ var sourceUriNode = sourceUriNodeWrapped switch
+ {
+ XamlAstNewClrObjectNode newObj => newObj,
+ XamlValueWithManipulationNode manipulation => manipulation.UnwrapValue(),
+ _ => null
+ };
+
+ // Validate Uri type and constant arguments.
+ if (sourceUriNode is null
|| sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri
|| sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath }
|| sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind })
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAddSourceInfoTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAddSourceInfoTransformer.cs
new file mode 100644
index 0000000000..1ce6e456d6
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAddSourceInfoTransformer.cs
@@ -0,0 +1,69 @@
+using System.Linq;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+using XamlX.Ast;
+using XamlX.Emit;
+using XamlX.IL;
+using XamlX.Transform;
+
+namespace Avalonia.Markup.Xaml.Loader.CompilerExtensions.Transformers
+{
+ ///
+ /// An XAMLIL AST transformer that injects metadata into the generated XAML code.
+ ///
+ ///
+ /// This transformer wraps object creation nodes with a manipulation node that adds source information.
+ /// This source information includes line number, position, and document name, which can be useful for debugging and diagnostics.
+ /// Note: ResourceDictionary source info is handled separately in .
+ ///
+ internal class AvaloniaXamlIlAddSourceInfoTransformer : IXamlAstTransformer
+ {
+ ///
+ /// Gets or sets a value indicating whether source information should be generated
+ /// and injected into the compiled XAML output.
+ ///
+ public bool CreateSourceInfo { get; set; }
+
+ public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
+ {
+ if (CreateSourceInfo
+ && node is XamlAstNewClrObjectNode objNode
+ && context.ParentNodes().FirstOrDefault() is not XamlValueWithManipulationNode { Manipulation: XamlSourceInfoValueManipulation }
+ && !objNode.Type.GetClrType().IsValueType)
+ {
+ var avaloniaTypes = context.GetAvaloniaTypes();
+
+ return new XamlValueWithManipulationNode(
+ objNode, objNode,
+ new XamlSourceInfoValueManipulation(avaloniaTypes, objNode, context.Document));
+ }
+
+ return node;
+ }
+
+ private class XamlSourceInfoValueManipulation(
+ AvaloniaXamlIlWellKnownTypes avaloniaTypes,
+ XamlAstNewClrObjectNode objNode, string? document)
+ : XamlAstNode(objNode), IXamlAstManipulationNode, IXamlAstILEmitableNode
+ {
+ public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen)
+ {
+ // Target object is already on stack.
+
+ // var info = new XamlSourceInfo(Line, Position, Document);
+ codeGen.Ldc_I4(Line);
+ codeGen.Ldc_I4(Position);
+ if (document is not null)
+ codeGen.Ldstr(document);
+ else
+ codeGen.Ldnull();
+ codeGen.Newobj(avaloniaTypes.XamlSourceInfoConstructor);
+
+ // Set the XamlSourceInfo property on the current object
+ // XamlSourceInfo.SetValue(@this, info);
+ codeGen.EmitCall(avaloniaTypes.XamlSourceInfoSetter);
+
+ return XamlILNodeEmitResult.Void(1);
+ }
+ }
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs
deleted file mode 100644
index 81a174c6e2..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs
+++ /dev/null
@@ -1,191 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Visitors;
-using XamlX;
-using XamlX.Ast;
-using XamlX.Emit;
-using XamlX.IL;
-using XamlX.Transform;
-using XamlX.TypeSystem;
-
-namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
-{
- internal class AvaloniaXamlIlDeferredResourceTransformer : IXamlAstTransformer
- {
- public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
- {
- if (!(node is XamlPropertyAssignmentNode pa) || pa.Values.Count != 2)
- return node;
-
- var types = context.GetAvaloniaTypes();
-
- if (pa.Property.DeclaringType == types.ResourceDictionary && pa.Property.Name == "Content"
- && ShouldBeDeferred(pa.Values[1]))
- {
- IXamlMethod addMethod = TryGetSharedValue(pa.Values[1], out var isShared) && !isShared
- ? types.ResourceDictionaryNotSharedDeferredAdd
- : types.ResourceDictionaryDeferredAdd;
-
- pa.Values[1] = new XamlDeferredContentNode(pa.Values[1], types.XamlIlTypes.Object, context.Configuration);
- pa.PossibleSetters = new List
- {
- new XamlDirectCallPropertySetter(addMethod),
- };
- }
- else if (pa.Property.Name == "Resources" && pa.Property.Getter?.ReturnType.Equals(types.IResourceDictionary) == true
- && ShouldBeDeferred(pa.Values[1]))
- {
- IXamlMethod addMethod = TryGetSharedValue(pa.Values[1], out var isShared) && !isShared
- ? types.ResourceDictionaryNotSharedDeferredAdd
- : types.ResourceDictionaryDeferredAdd;
-
- pa.Values[1] = new XamlDeferredContentNode(pa.Values[1], types.XamlIlTypes.Object, context.Configuration);
- pa.PossibleSetters = new List
- {
- new AdderSetter(pa.Property.Getter, addMethod),
- };
- }
-
- return node;
-
- bool TryGetSharedValue(IXamlAstValueNode valueNode, out bool value)
- {
- value = default;
- if (valueNode is XamlAstConstructableObjectNode co)
- {
- // Try find x:Share directive
- if (co.Children.Find(d => d is XamlAstXmlDirective { Namespace: XamlNamespaces.Xaml2006, Name: "Shared" }) is XamlAstXmlDirective sharedDirective)
- {
- if (sharedDirective.Values.Count == 1 && sharedDirective.Values[0] is XamlAstTextNode text)
- {
- if (bool.TryParse(text.Text, out var parseValue))
- {
- // If the parser succeeds, remove the x:Share directive
- co.Children.Remove(sharedDirective);
- return true;
- }
- else
- {
- context.ReportTransformError("Invalid argument type for x:Shared directive.", node);
- }
- }
- else
- {
- context.ReportTransformError("Invalid number of arguments for x:Shared directive.", node);
- }
- }
- }
- return false;
- }
- }
-
- private static bool ShouldBeDeferred(IXamlAstValueNode node)
- {
- var clrType = node.Type.GetClrType();
-
- // XAML compiler is currently strict about value types, allowing them to be created only through converters.
- // At the moment it should be safe to not defer structs.
- if (clrType.IsValueType)
- {
- return false;
- }
-
- // Never defer strings.
- if (clrType.FullName == "System.String")
- {
- return false;
- }
-
- // Do not defer resources, if it has any x:Name registration, as it cannot be delayed.
- // This visitor will count x:Name registrations, ignoring nested NestedScopeMetadataNode scopes.
- // We set target scope level to 0, assuming that this resource node is a scope of itself.
- var nameRegistrationsVisitor = new NameScopeRegistrationVisitor(
- targetMetadataScopeLevel: 0);
- node.Visit(nameRegistrationsVisitor);
- if (nameRegistrationsVisitor.Count > 0)
- {
- return false;
- }
-
- return true;
- }
-
- class AdderSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable
- {
- private readonly IXamlMethod _getter;
- private readonly IXamlMethod _adder;
-
- public AdderSetter(IXamlMethod getter, IXamlMethod adder)
- {
- _getter = getter;
- _adder = adder;
- TargetType = getter.DeclaringType;
- Parameters = adder.ParametersWithThis().Skip(1).ToList();
-
- bool allowNull = Parameters.Last().AcceptsNull();
- BinderParameters = new PropertySetterBinderParameters
- {
- AllowMultiple = true,
- AllowXNull = allowNull,
- AllowRuntimeNull = allowNull,
- AllowAttributeSyntax = false,
- };
- }
-
- public IXamlType TargetType { get; }
-
- public PropertySetterBinderParameters BinderParameters { get; }
-
- public IReadOnlyList Parameters { get; }
- public IReadOnlyList CustomAttributes => _adder.CustomAttributes;
-
- public void Emit(IXamlILEmitter emitter)
- {
- var locals = new Stack();
- // Save all "setter" parameters
- for (var c = Parameters.Count - 1; c >= 0; c--)
- {
- var loc = emitter.LocalsPool.GetLocal(Parameters[c]);
- locals.Push(loc);
- emitter.Stloc(loc.Local);
- }
-
- emitter.EmitCall(_getter);
- while (locals.Count>0)
- using (var loc = locals.Pop())
- emitter.Ldloc(loc.Local);
- emitter.EmitCall(_adder, true);
- }
-
- public void EmitWithArguments(
- XamlEmitContextWithLocals context,
- IXamlILEmitter emitter,
- IReadOnlyList arguments)
- {
- emitter.EmitCall(_getter);
-
- for (var i = 0; i < arguments.Count; ++i)
- context.Emit(arguments[i], emitter, Parameters[i]);
-
- emitter.EmitCall(_adder, true);
- }
-
- public bool Equals(AdderSetter? other)
- {
- if (ReferenceEquals(null, other))
- return false;
- if (ReferenceEquals(this, other))
- return true;
-
- return _getter.Equals(other._getter) && _adder.Equals(other._adder);
- }
-
- public override bool Equals(object? obj)
- => Equals(obj as AdderSetter);
-
- public override int GetHashCode()
- => (_getter.GetHashCode() * 397) ^ _adder.GetHashCode();
- }
- }
-}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlResourceTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlResourceTransformer.cs
new file mode 100644
index 0000000000..4e2458696e
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlResourceTransformer.cs
@@ -0,0 +1,343 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Visitors;
+using XamlX;
+using XamlX.Ast;
+using XamlX.Emit;
+using XamlX.IL;
+using XamlX.Transform;
+using XamlX.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
+{
+ ///
+ /// Transforms ResourceDictionary and IResourceDictionary property assignments
+ /// to use Add method calls with deferred content where applicable.
+ /// Additionally, handles x:Shared on assignments and injects XamlSourceInfo.
+ ///
+ internal class AvaloniaXamlResourceTransformer : IXamlAstTransformer
+ {
+ ///
+ /// Gets or sets a value indicating whether source information should be generated
+ /// and injected into the compiled XAML output.
+ ///
+ public bool CreateSourceInfo { get; set; } = true;
+
+ public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
+ {
+ if (!(node is XamlPropertyAssignmentNode pa) || pa.Values.Count != 2)
+ return node;
+
+ var types = context.GetAvaloniaTypes();
+ var document = context.Document;
+
+ if (pa.Property.DeclaringType == types.ResourceDictionary && pa.Property.Name == "Content")
+ {
+ var value = pa.Values[1];
+ (var adder, value) = ResolveAdderAndValue(value);
+
+ pa.Values[1] = value;
+ pa.PossibleSetters = new List
+ {
+ new AdderSetter(adder, CreateSourceInfo, types, value.Line, value.Position, document),
+ };
+ }
+ else if (pa.Property.Name == "Resources" && pa.Property.Getter?.ReturnType.Equals(types.IResourceDictionary) == true)
+ {
+ var value = pa.Values[1];
+ (var adder, value) = ResolveAdderAndValue(value);
+
+ pa.Values[1] = value;
+ pa.PossibleSetters = new List
+ {
+ new AdderSetter(pa.Property.Getter, adder, CreateSourceInfo, types, value.Line, value.Position, document),
+ };
+ }
+
+ return node;
+
+ (IXamlMethod adder, IXamlAstValueNode newValue) ResolveAdderAndValue(IXamlAstValueNode valueNode)
+ {
+ if (ShouldBeDeferred(valueNode))
+ {
+ var adder = TryGetSharedValue(valueNode, out var isShared) && !isShared
+ ? types.ResourceDictionaryNotSharedDeferredAdd
+ : types.ResourceDictionaryDeferredAdd;
+ var deferredNode = new XamlDeferredContentNode(valueNode, types.XamlIlTypes.Object, context.Configuration);
+ return (adder, deferredNode);
+ }
+ else
+ {
+ var adder = XamlTransformHelpers.FindPossibleAdders(context, types.IResourceDictionary)
+ .FirstOrDefault() ?? throw new XamlTransformException("No suitable Add method found for IResourceDictionary.", node);
+ return (adder, valueNode);
+ }
+ }
+
+ bool TryGetSharedValue(IXamlAstValueNode valueNode, out bool value)
+ {
+ value = default;
+ if (valueNode is XamlAstConstructableObjectNode co)
+ {
+ // Try find x:Share directive
+ if (co.Children.Find(d => d is XamlAstXmlDirective { Namespace: XamlNamespaces.Xaml2006, Name: "Shared" }) is XamlAstXmlDirective sharedDirective)
+ {
+ if (sharedDirective.Values.Count == 1 && sharedDirective.Values[0] is XamlAstTextNode text)
+ {
+ if (bool.TryParse(text.Text, out var parseValue))
+ {
+ // If the parser succeeds, remove the x:Share directive
+ co.Children.Remove(sharedDirective);
+ return true;
+ }
+ else
+ {
+ context.ReportTransformError("Invalid argument type for x:Shared directive.", node);
+ }
+ }
+ else
+ {
+ context.ReportTransformError("Invalid number of arguments for x:Shared directive.", node);
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ private static bool ShouldBeDeferred(IXamlAstValueNode node)
+ {
+ var clrType = node.Type.GetClrType();
+
+ // XAML compiler is currently strict about value types, allowing them to be created only through converters.
+ // At the moment it should be safe to not defer structs.
+ if (clrType.IsValueType)
+ {
+ return false;
+ }
+
+ // Never defer strings.
+ if (clrType.FullName == "System.String")
+ {
+ return false;
+ }
+
+ // Do not defer resources, if it has any x:Name registration, as it cannot be delayed.
+ // This visitor will count x:Name registrations, ignoring nested NestedScopeMetadataNode scopes.
+ // We set target scope level to 0, assuming that this resource node is a scope of itself.
+ var nameRegistrationsVisitor = new NameScopeRegistrationVisitor(
+ targetMetadataScopeLevel: 0);
+ node.Visit(nameRegistrationsVisitor);
+ if (nameRegistrationsVisitor.Count > 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ class AdderSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable
+ {
+ private readonly IXamlMethod? _getter;
+ private readonly IXamlMethod _adder;
+ private readonly bool _emitSourceInfo;
+ private readonly AvaloniaXamlIlWellKnownTypes _avaloniaTypes;
+ private readonly string? _document;
+ private readonly int _line, _position;
+
+ ///
+ /// Creates an adder-only setter. Target is assumed to be already on the stack before emit.
+ /// For example:
+ /// var resourceDictionary = ...
+ /// resourceDictionary.Add(key, value);
+ /// resourceDictionary.Add(key2, value2);
+ ///
+ public AdderSetter(
+ IXamlMethod adder,
+ bool emitSourceInfo,
+ AvaloniaXamlIlWellKnownTypes avaloniaTypes,
+ int line, int position, string? document)
+ {
+ _adder = adder;
+ _emitSourceInfo = emitSourceInfo;
+ _avaloniaTypes = avaloniaTypes;
+ _line = line;
+ _position = position;
+ _document = document;
+
+ TargetType = adder.ThisOrFirstParameter();
+ Parameters = adder.ParametersWithThis().Skip(1).ToList();
+ bool allowNull = Parameters.Last().AcceptsNull();
+ BinderParameters = new PropertySetterBinderParameters
+ {
+ AllowMultiple = true,
+ AllowXNull = allowNull,
+ AllowRuntimeNull = allowNull
+ };
+ }
+
+ ///
+ /// Explicit target getter - target will be obtained by calling the getter first.
+ ///
+ ///
+ public AdderSetter(
+ IXamlMethod getter, IXamlMethod adder,
+ bool emitSourceInfo,
+ AvaloniaXamlIlWellKnownTypes avaloniaTypes,
+ int line, int position, string? document)
+ : this(adder, emitSourceInfo, avaloniaTypes, line, position, document)
+ {
+ _getter = getter;
+ TargetType = getter.DeclaringType;
+ BinderParameters.AllowMultiple = false;
+ BinderParameters.AllowAttributeSyntax = false;
+ }
+
+ public IXamlType TargetType { get; }
+
+ public PropertySetterBinderParameters BinderParameters { get; }
+
+ public IReadOnlyList Parameters { get; }
+ public IReadOnlyList CustomAttributes => _adder.CustomAttributes;
+
+ ///
+ /// Emits the setter with arguments already on the stack.
+ ///
+ ///
+ /// If _getter is null - assume target is already on the stack.
+ /// In this case, we can just call Emit. Unless _emitSourceInfo is true.
+ ///
+ /// If _emitSourceInfo is true - we need to make sure that target and key are on the stack for XamlSourceInfo setting,
+ /// so we need to store parameters to locals first regardless.
+ ///
+ public void Emit(IXamlILEmitter emitter)
+ {
+ using var keyLocal = emitter.LocalsPool.GetLocal(Parameters[0]);
+
+ if (_getter is not null || _emitSourceInfo)
+ {
+ var locals = new Stack();
+ // Save all "setter" parameters
+ for (var c = Parameters.Count - 1; c >= 0; c--)
+ {
+ var loc = emitter.LocalsPool.GetLocal(Parameters[c]);
+ locals.Push(loc);
+ emitter.Stloc(loc.Local);
+
+ if (c == 0 && _emitSourceInfo)
+ {
+ // Store the key argument for XamlSourceInfo later
+ emitter.Ldloc(loc.Local);
+ emitter.Stloc(keyLocal.Local);
+ }
+ }
+
+ if (_getter is not null)
+ {
+ emitter.EmitCall(_getter);
+ }
+
+ // Duplicate the target object on stack for setting XamlSourceInfo later
+ emitter.Dup();
+
+ while (locals.Count > 0)
+ using (var loc = locals.Pop())
+ emitter.Ldloc(loc.Local);
+ }
+
+ emitter.EmitCall(_adder, true);
+
+ if (_emitSourceInfo)
+ {
+ // Target is already on stack (dup)
+ // Load the key argument from local
+ emitter.Ldloc(keyLocal.Local);
+ EmitSetSourceInfo(emitter);
+ }
+ }
+
+ ///
+ /// Emits the setter with provided arguments that are not yet on the stack.
+ ///
+ ///
+ /// If _getter is null - assume target is already on the stack.
+ /// If _emitSourceInfo is true - we need to make sure that target and key are on the stack for XamlSourceInfo setting.
+ ///
+ public void EmitWithArguments(
+ XamlEmitContextWithLocals context,
+ IXamlILEmitter emitter,
+ IReadOnlyList arguments)
+ {
+ using var keyLocal = _emitSourceInfo ? emitter.LocalsPool.GetLocal(Parameters[0]) : null;
+
+ if (_getter is not null)
+ {
+ emitter.EmitCall(_getter);
+ }
+
+ if (_emitSourceInfo)
+ {
+ // Duplicate the target object on stack for setting XamlSourceInfo later
+ emitter.Dup();
+ }
+
+ for (var i = 0; i < arguments.Count; ++i)
+ {
+ context.Emit(arguments[i], emitter, Parameters[i]);
+
+ // Store the key argument for XamlSourceInfo later
+ if (i == 0 && _emitSourceInfo)
+ {
+ emitter.Stloc(keyLocal!.Local);
+ emitter.Ldloc(keyLocal.Local);
+ }
+ }
+
+ emitter.EmitCall(_adder, true);
+
+ if (_emitSourceInfo)
+ {
+ // Target is already on stack (dub)
+ // Load the key argument from local
+ emitter.Ldloc(keyLocal!.Local);
+
+ EmitSetSourceInfo(emitter);
+ }
+ }
+
+ private void EmitSetSourceInfo(IXamlILEmitter emitter)
+ {
+ // Assumes the target object and key are already on the stack
+
+ emitter.Ldc_I4(_line);
+ emitter.Ldc_I4(_position);
+ if (_document is not null)
+ emitter.Ldstr(_document);
+ else
+ emitter.Ldnull();
+ emitter.Newobj(_avaloniaTypes.XamlSourceInfoConstructor);
+
+ // Set the XamlSourceInfo property on the current object
+ // XamlSourceInfo.SetXamlSourceInfo(@this, key, info);
+ emitter.EmitCall(_avaloniaTypes.XamlSourceInfoDictionarySetter);
+ }
+
+ public bool Equals(AdderSetter? other)
+ {
+ if (ReferenceEquals(null, other))
+ return false;
+ if (ReferenceEquals(this, other))
+ return true;
+
+ return _getter?.Equals(other._getter) == true && _adder.Equals(other._adder);
+ }
+
+ public override bool Equals(object? obj)
+ => Equals(obj as AdderSetter);
+
+ public override int GetHashCode()
+ => (_getter, _adder).GetHashCode();
+ }
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
index 8659eb1299..df54e71108 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
@@ -134,6 +134,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType ControlTemplate { get; }
public IXamlType EventHandlerT { get; }
public IXamlMethod GetClassProperty { get; }
+ public IXamlConstructor XamlSourceInfoConstructor { get; }
+ public IXamlMethod XamlSourceInfoSetter { get; }
+ public IXamlMethod XamlSourceInfoDictionarySetter { get; }
sealed internal class InteractivityWellKnownTypes
{
@@ -343,6 +346,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
allowDowncast:false,
cfg.WellKnownTypes.String
);
+
+ var xamlSourceInfo = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Diagnostics.XamlSourceInfo");
+ XamlSourceInfoConstructor = xamlSourceInfo.GetConstructor([
+ XamlIlTypes.Int32, XamlIlTypes.Int32, XamlIlTypes.String
+ ]);
+ XamlSourceInfoSetter =
+ xamlSourceInfo.GetMethod("SetXamlSourceInfo", XamlIlTypes.Void, false, XamlIlTypes.Object, xamlSourceInfo);
+ XamlSourceInfoDictionarySetter =
+ xamlSourceInfo.GetMethod("SetXamlSourceInfo", XamlIlTypes.Void, false, IResourceDictionary, XamlIlTypes.Object, xamlSourceInfo);
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlAstNewClrObjectHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlAstNewClrObjectHelper.cs
new file mode 100644
index 0000000000..612f675124
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlAstNewClrObjectHelper.cs
@@ -0,0 +1,26 @@
+using XamlX.Ast;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
+
+internal static class XamlAstNewClrObjectHelper
+{
+ ///
+ /// Tries to resolve the underlying value of a ,
+ /// unwrapping any nested instances.
+ ///
+ public static TXamlAstValueNode? UnwrapValue(this XamlValueWithManipulationNode node)
+ where TXamlAstValueNode : class, IXamlAstValueNode
+ {
+ var current = node.Value;
+ while (current is XamlValueWithManipulationNode valueWithManipulation)
+ {
+ current = valueWithManipulation.Value;
+ if (current is TXamlAstValueNode typedValue)
+ {
+ return typedValue;
+ }
+ }
+
+ return current as TXamlAstValueNode;
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index e6186bbea6..6dd2690414 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -38,6 +38,7 @@
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/Diagnostics/XamlSourceInfo.cs b/src/Markup/Avalonia.Markup.Xaml/Diagnostics/XamlSourceInfo.cs
new file mode 100644
index 0000000000..1bb5f1c98c
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/Diagnostics/XamlSourceInfo.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+
+namespace Avalonia.Markup.Xaml.Diagnostics
+{
+ ///
+ /// Represents source location information for an element within a XAML or code file.
+ ///
+ // ReSharper disable once ClassNeverInstantiated.Global //This class is instantiated through the XAML compiler.
+ public record XamlSourceInfo
+ {
+ private static readonly AttachedProperty s_xamlSourceInfo =
+ AvaloniaProperty.RegisterAttached(
+ "XamlSourceInfo", typeof(XamlSourceInfo));
+
+ private static readonly ConditionalWeakTable
public bool DesignMode { get; set; } = false;
+ ///
+ /// When enabled, the XAML compiler embeds SourceInfo metadata (file path, line, and column) into generated code.
+ /// Default is 'false'.
+ ///
+ public bool CreateSourceInfo { get; set; } = false;
+
///
/// XAML diagnostics handler.
///
diff --git a/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs b/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs
index 5fabc6ee35..937f64f14e 100644
--- a/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs
@@ -58,6 +58,11 @@ public class RuntimeXamlLoaderDocument
///
public Uri? BaseUri { get; set; }
+ ///
+ /// Path to the XAML document being loaded.
+ ///
+ public string? Document { get; set; }
+
///
/// The optional instance into which the XAML should be loaded.
///
diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
index 318cdac22c..46be609ec0 100644
--- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
@@ -67,7 +67,7 @@ namespace Avalonia.Skia
var framebuffer = _renderTargetWithProperties?.Lock(out lockProperties) ?? _renderTarget.Lock();
var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
framebuffer.Format.ToSkColorType(),
- framebuffer.Format == PixelFormat.Rgb565 ? SKAlphaType.Opaque : SKAlphaType.Premul);
+ framebuffer.AlphaFormat.ToSkAlphaType());
CreateSurface(framebufferImageInfo, framebuffer);
_hadConversionShim |= _conversionShim != null;
diff --git a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
index 0372f25047..44408a5333 100644
--- a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
+++ b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Skia
///
/// Immutable Skia bitmap.
///
- internal class ImmutableBitmap : IDrawableBitmapImpl, IReadableBitmapWithAlphaImpl
+ internal class ImmutableBitmap : IDrawableBitmapImpl, IReadableBitmapImpl
{
private readonly SKImage _image;
private readonly SKBitmap? _bitmap;
@@ -195,7 +195,9 @@ namespace Avalonia.Skia
if (_bitmap.ColorType.ToAvalonia() is not { } format)
throw new NotSupportedException($"Unsupported format {_bitmap.ColorType}");
- return new LockedFramebuffer(_bitmap.GetPixels(), PixelSize, _bitmap.RowBytes, Dpi, format, null);
+ var alphaFormat = _bitmap.AlphaType.ToAlphaFormat();
+
+ return new LockedFramebuffer(_bitmap.GetPixels(), PixelSize, _bitmap.RowBytes, Dpi, format, alphaFormat, null);
}
}
}
diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
index 7d8b247ad4..f57f84b168 100644
--- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -238,6 +238,8 @@ namespace Avalonia.Skia
public Vector Dpi => _parent.Dpi;
///
public PixelFormat Format => _bitmap.ColorType.ToPixelFormat();
+
+ public AlphaFormat AlphaFormat => _bitmap.AlphaType.ToAlphaFormat();
}
}
}
diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs
index 5c8e84aaa6..7c11277c57 100644
--- a/src/Windows/Avalonia.Win32/FramebufferManager.cs
+++ b/src/Windows/Avalonia.Win32/FramebufferManager.cs
@@ -49,7 +49,7 @@ namespace Avalonia.Win32
return fb = new LockedFramebuffer(
framebufferData.Data.Address, framebufferData.Size, framebufferData.RowBytes,
- GetCurrentDpi(), s_format, _onDisposeAction);
+ GetCurrentDpi(), s_format, AlphaFormat.Premul, _onDisposeAction);
}
finally
{
diff --git a/src/Windows/Avalonia.Win32/Interop/Win32Icon.cs b/src/Windows/Avalonia.Win32/Interop/Win32Icon.cs
index 57da43da92..1fe301526b 100644
--- a/src/Windows/Avalonia.Win32/Interop/Win32Icon.cs
+++ b/src/Windows/Avalonia.Win32/Interop/Win32Icon.cs
@@ -96,14 +96,14 @@ internal class Win32Icon : IDisposable
static IntPtr CreateHBitmap(Bitmap source)
{
- using var fb = AllocFramebuffer(source.PixelSize, PixelFormats.Bgra8888);
- source.CopyPixels(fb, AlphaFormat.Unpremul);
+ using var fb = AllocFramebuffer(source.PixelSize, PixelFormats.Bgra8888, AlphaFormat.Unpremul);
+ source.CopyPixels(fb);
return UnmanagedMethods.CreateBitmap(source.PixelSize.Width, source.PixelSize.Height, 1, 32, fb.Address);
}
static unsafe IntPtr AlphaToMask(Bitmap source)
{
- using var alphaMaskBuffer = AllocFramebuffer(source.PixelSize, PixelFormats.BlackWhite);
+ using var alphaMaskBuffer = AllocFramebuffer(source.PixelSize, PixelFormats.BlackWhite, AlphaFormat.Opaque);
var height = alphaMaskBuffer.Size.Height;
var width = alphaMaskBuffer.Size.Width;
@@ -114,8 +114,8 @@ internal class Win32Icon : IDisposable
}
else
{
- using var argbBuffer = AllocFramebuffer(source.PixelSize, PixelFormat.Bgra8888);
- source.CopyPixels(argbBuffer, AlphaFormat.Unpremul);
+ using var argbBuffer = AllocFramebuffer(source.PixelSize, PixelFormat.Bgra8888, AlphaFormat.Unpremul);
+ source.CopyPixels(argbBuffer);
var pSource = (byte*)argbBuffer.Address;
var pDest = (byte*)alphaMaskBuffer.Address;
@@ -140,7 +140,7 @@ internal class Win32Icon : IDisposable
return UnmanagedMethods.CreateBitmap(width, height, 1, 1, alphaMaskBuffer.Address);
}
- static LockedFramebuffer AllocFramebuffer(PixelSize size, PixelFormat format)
+ static LockedFramebuffer AllocFramebuffer(PixelSize size, PixelFormat format, AlphaFormat alphaFormat)
{
if (size.Width < 1 || size.Height < 1)
throw new ArgumentOutOfRangeException();
@@ -149,7 +149,7 @@ internal class Win32Icon : IDisposable
var data = Marshal.AllocHGlobal(size.Height * stride);
if (data == IntPtr.Zero)
throw new OutOfMemoryException();
- return new LockedFramebuffer(data, size, stride, new Vector(96, 96), format,
+ return new LockedFramebuffer(data, size, stride, new Vector(96, 96), format, alphaFormat,
() => Marshal.FreeHGlobal(data));
}
diff --git a/tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs
deleted file mode 100644
index b7d09a40d7..0000000000
--- a/tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using System;
-using Avalonia.Utilities;
-using Xunit;
-
-#pragma warning disable CS0618 // Type or member is obsolete
-
-namespace Avalonia.Base.UnitTests.Utilities
-{
- public class StringTokenizerTests
- {
- [Fact]
- public void ReadInt32_Reads_Values()
- {
- var target = new StringTokenizer("123,456");
-
- Assert.Equal(123, target.ReadInt32());
- Assert.Equal(456, target.ReadInt32());
- Assert.Throws(() => target.ReadInt32());
- }
-
- [Fact]
- public void ReadDouble_Reads_Values()
- {
- var target = new StringTokenizer("12.3,45.6");
-
- Assert.Equal(12.3, target.ReadDouble());
- Assert.Equal(45.6, target.ReadDouble());
- Assert.Throws(() => target.ReadDouble());
- }
-
- [Fact]
- public void TryReadInt32_Reads_Values()
- {
- var target = new StringTokenizer("123,456");
-
- Assert.True(target.TryReadInt32(out var value));
- Assert.Equal(123, value);
- Assert.True(target.TryReadInt32(out value));
- Assert.Equal(456, value);
- Assert.False(target.TryReadInt32(out value));
- }
-
- [Fact]
- public void TryReadInt32_Doesnt_Throw()
- {
- var target = new StringTokenizer("abc");
-
- Assert.False(target.TryReadInt32(out var value));
- }
-
- [Fact]
- public void TryReadDouble_Reads_Values()
- {
- var target = new StringTokenizer("12.3,45.6");
-
- Assert.True(target.TryReadDouble(out var value));
- Assert.Equal(12.3, value);
- Assert.True(target.TryReadDouble(out value));
- Assert.Equal(45.6, value);
- Assert.False(target.TryReadDouble(out value));
- }
-
- [Fact]
- public void TryReadDouble_Doesnt_Throw()
- {
- var target = new StringTokenizer("abc");
-
- Assert.False(target.TryReadDouble(out var value));
- }
-
- [Fact]
- public void ReadSpan_And_ReadString_Reads_Same()
- {
- var target1 = new StringTokenizer("abc,def");
- var target2 = new StringTokenizer("abc,def");
-
- Assert.Equal(target1.ReadString(), target2.ReadSpan().ToString());
- Assert.True(target1.ReadSpan().SequenceEqual(target2.ReadString()));
- }
- }
-}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs
index 624e14899b..8949df2eb4 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs
@@ -12,8 +12,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{
public class ResourceIncludeTests : XamlTestBase
{
- [Fact]
- public void ResourceInclude_Loads_ResourceDictionary()
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void ResourceInclude_Loads_ResourceDictionary(bool createSourceInfo)
{
var documents = new[]
{
@@ -37,9 +39,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
")
};
+ var config = new RuntimeXamlLoaderConfiguration { CreateSourceInfo = createSourceInfo };
+
using (StartWithResources())
{
- var compiled = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
+ var compiled = AvaloniaRuntimeXamlLoader.LoadGroup(documents, config);
var userControl = Assert.IsType(compiled[1]);
var border = userControl.GetControl("border");
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
index 608e10f739..c805619f44 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
@@ -18,8 +18,10 @@ public class MergeResourceIncludeTests : XamlTestBase
RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle);
}
- [Fact]
- public void MergeResourceInclude_Works_With_Single_Resource()
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void MergeResourceInclude_Works_With_Single_Resource(bool createSourceInfo)
{
var documents = new[]
{
@@ -41,8 +43,9 @@ public class MergeResourceIncludeTests : XamlTestBase
")
};
-
- var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
+
+ var config = new RuntimeXamlLoaderConfiguration { CreateSourceInfo = createSourceInfo };
+ var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents, config);
var contentControl = Assert.IsType(objects[1]);
var resources = Assert.IsType(contentControl.Resources);
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
index b28e049191..1fdbd9bcc3 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
@@ -265,6 +265,8 @@ public class StyleIncludeTests : XamlTestBase
[Fact]
public void StyleInclude_Should_Be_Replaced_With_Direct_Call()
{
+ using var _ = UnitTestApplication.Start(TestServices.StyledWindow);
+
var control = (ContentControl)AvaloniaRuntimeXamlLoader.Load(@"
();
var control = (ContentControl)AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(@"
+
+ """)
+ {
+ Document = document
+ };
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xamlDocument, s_configuration);
+
+ var sourceInfo = XamlSourceInfo.GetXamlSourceInfo(userControl);
+
+ Assert.NotNull(sourceInfo);
+ Assert.Equal("file", sourceInfo.SourceUri!.Scheme);
+ Assert.True(sourceInfo.SourceUri!.IsAbsoluteUri);
+ Assert.Equal(new UriBuilder("file", "") {Path = document}.Uri, sourceInfo.SourceUri);
+ }
+
+ [Fact]
+ public void Root_UserControl_Gets_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+
+ var sourceInfo = XamlSourceInfo.GetXamlSourceInfo(userControl);
+
+ Assert.NotNull(sourceInfo);
+ }
+
+ [Fact]
+ public void Nested_Controls_All_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var stackPanel = (StackPanel)userControl.Content!;
+ var button = (Button)stackPanel.Children[0];
+ var textblock = (TextBlock)stackPanel.Children[1];
+
+ var userControlSourceInfo = XamlSourceInfo.GetXamlSourceInfo(userControl);
+ Assert.NotNull(userControlSourceInfo);
+
+ var stackPanelSourceInfo = XamlSourceInfo.GetXamlSourceInfo(stackPanel);
+ Assert.NotNull(stackPanelSourceInfo);
+
+ var buttonSourceInfo = XamlSourceInfo.GetXamlSourceInfo(button);
+ Assert.NotNull(buttonSourceInfo);
+
+ var textblockSourceInfo = XamlSourceInfo.GetXamlSourceInfo(textblock);
+ Assert.NotNull(textblockSourceInfo);
+ }
+
+ [Fact]
+ public void Property_Elements_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+
+
+
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var rect = (Rectangle)userControl.Content!;
+ var gradient = (LinearGradientBrush)rect.OpacityMask!;
+ var stopOne = (GradientStop)gradient.GradientStops.First();
+ var stopTwo = (GradientStop)gradient.GradientStops.Last();
+
+ var rectSourceInfo = XamlSourceInfo.GetXamlSourceInfo(rect);
+ Assert.NotNull(rectSourceInfo);
+
+ var gradientSourceInfo = XamlSourceInfo.GetXamlSourceInfo(gradient);
+ Assert.NotNull(gradientSourceInfo);
+
+ var stopOneSourceInfo = XamlSourceInfo.GetXamlSourceInfo(stopOne);
+ Assert.NotNull(stopOneSourceInfo);
+
+ var stopTwoSourceInfo = XamlSourceInfo.GetXamlSourceInfo(stopTwo);
+ Assert.NotNull(stopTwoSourceInfo);
+ }
+
+ [Fact]
+ public void Shapes_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var canvas = (Canvas)userControl.Content!;
+ var ellipse = (Ellipse)canvas.Children[0];
+ var path1 = (Path)canvas.Children[1];
+ var path2 = (Path)canvas.Children[2];
+ var geometry = (PathGeometry)path2.Data!;
+ var figure = (PathFigure)geometry.Figures![0];
+ var segment1 = figure.Segments![0];
+ var segment2 = figure.Segments![1];
+ var segment3 = figure.Segments![2];
+ var segment4 = figure.Segments![3];
+ var line = (Line)canvas.Children[3];
+ var polygon = (Polygon)canvas.Children[4];
+
+ var canvasSourceInfo = XamlSourceInfo.GetXamlSourceInfo(canvas);
+ Assert.NotNull(canvasSourceInfo);
+
+ var ellipseSourceInfo = XamlSourceInfo.GetXamlSourceInfo(ellipse);
+ Assert.NotNull(ellipseSourceInfo);
+
+ var path1SourceInfo = XamlSourceInfo.GetXamlSourceInfo(path1);
+ Assert.NotNull(path1SourceInfo);
+
+ var path2SourceInfo = XamlSourceInfo.GetXamlSourceInfo(path2);
+ Assert.NotNull(path2SourceInfo);
+
+ var geometrySourceInfo = XamlSourceInfo.GetXamlSourceInfo(geometry);
+ Assert.NotNull(geometrySourceInfo);
+
+ var figureSourceInfo = XamlSourceInfo.GetXamlSourceInfo(figure);
+ Assert.NotNull(figureSourceInfo);
+
+ var segment1SourceInfo = XamlSourceInfo.GetXamlSourceInfo(segment1);
+ Assert.NotNull(segment1SourceInfo);
+
+ var segment2SourceInfo = XamlSourceInfo.GetXamlSourceInfo(segment2);
+ Assert.NotNull(segment2SourceInfo);
+
+ var segment3SourceInfo = XamlSourceInfo.GetXamlSourceInfo(segment3);
+ Assert.NotNull(segment3SourceInfo);
+
+ var segment4SourceInfo = XamlSourceInfo.GetXamlSourceInfo(segment4);
+ Assert.NotNull(segment4SourceInfo);
+
+ var lineSourceInfo = XamlSourceInfo.GetXamlSourceInfo(line);
+ Assert.NotNull(lineSourceInfo);
+
+ var polygonSourceInfo = XamlSourceInfo.GetXamlSourceInfo(polygon);
+ Assert.NotNull(polygonSourceInfo);
+ }
+
+ [Fact]
+ public void Styles_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var style = (Style)userControl.Styles[0];
+ var query = (ContainerQuery)userControl.Styles[1];
+
+ var styleSourceInfo = XamlSourceInfo.GetXamlSourceInfo(style);
+ Assert.NotNull(styleSourceInfo);
+
+ var querySourceInfo = XamlSourceInfo.GetXamlSourceInfo(query);
+ Assert.NotNull(querySourceInfo);
+ }
+
+ [Fact]
+ public void Animations_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var style = (Style)userControl.Styles[0];
+ var animation = (Animation.Animation)style.Animations[0];
+ var frame1 = animation.Children[0];
+ var frame2 = animation.Children[1];
+
+ var styleSourceInfo = XamlSourceInfo.GetXamlSourceInfo(style);
+ Assert.NotNull(styleSourceInfo);
+
+ var animationSourceInfo = XamlSourceInfo.GetXamlSourceInfo(animation);
+ Assert.NotNull(animationSourceInfo);
+
+ var frameOneSourceInfo = XamlSourceInfo.GetXamlSourceInfo(frame1);
+ Assert.NotNull(frameOneSourceInfo);
+
+ var frameTwoSourceInfo = XamlSourceInfo.GetXamlSourceInfo(frame2);
+ Assert.NotNull(frameTwoSourceInfo);
+ }
+
+ [Fact]
+ public void DataTemplates_And_Deferred_Contents_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var datatemplate = (DataTemplate)userControl.DataTemplates[0];
+
+ Border border;
+
+ // The template and it's content is deferred as not used (yet)
+ if (datatemplate.Content is IDeferredContent deferredContent)
+ {
+ var templateResult = (ITemplateResult)deferredContent.Build(null)!;
+ border = (Border)templateResult.Result!;
+ }
+ else
+ {
+ border = (Border)datatemplate.Content!;
+ }
+
+ var textBox = (TextBox)border!.Child!;
+
+ var datatemplateSourceInfo = XamlSourceInfo.GetXamlSourceInfo(datatemplate);
+ Assert.NotNull(datatemplateSourceInfo);
+
+ var borderSourceInfo = XamlSourceInfo.GetXamlSourceInfo(border);
+ Assert.NotNull(borderSourceInfo);
+
+ var textBoxSourceInfo = XamlSourceInfo.GetXamlSourceInfo(textBox);
+ Assert.NotNull(textBoxSourceInfo);
+ }
+
+ [Fact]
+ public void Resources_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Black
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var backgroundBrush = userControl.Resources["Background"];
+ var lightDictionary = (ResourceDictionary)userControl.Resources.ThemeDictionaries[ThemeVariant.Light];
+ var darkDictionary = (ResourceDictionary)userControl.Resources.ThemeDictionaries[ThemeVariant.Dark];
+ var lightForeground = lightDictionary["ForegroundBrush"];
+ var darkBackground = lightDictionary["BackgroundBrush"];
+ var otherBrush = userControl.Resources["OtherBrush"];
+
+ var backgroundBrushSourceInfo = XamlSourceInfo.GetXamlSourceInfo(backgroundBrush!);
+ Assert.NotNull(backgroundBrushSourceInfo);
+
+ var lightDictionarySourceInfo = XamlSourceInfo.GetXamlSourceInfo(lightDictionary!);
+ Assert.NotNull(lightDictionarySourceInfo);
+
+ var darkDictionarySourceInfo = XamlSourceInfo.GetXamlSourceInfo(darkDictionary!);
+ Assert.NotNull(darkDictionarySourceInfo);
+
+ var lightForegroundSourceInfo = XamlSourceInfo.GetXamlSourceInfo(lightForeground!);
+ Assert.NotNull(lightForegroundSourceInfo);
+
+ var darkBackgroundSourceInfo = XamlSourceInfo.GetXamlSourceInfo(darkBackground!);
+ Assert.NotNull(darkBackgroundSourceInfo);
+
+ var otherBrushSourceInfo = XamlSourceInfo.GetXamlSourceInfo(otherBrush!);
+ Assert.NotNull(otherBrushSourceInfo);
+ }
+
+ [Fact]
+ public void ResourceDictionary_Value_Types_Do_Not_Set_XamlSourceInfo()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+ foobar
+ 123.3
+ 123
+ 37434323
+ 10,20,10,0
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var foobarString = userControl.Resources["text"];
+ var aDouble = userControl.Resources["A_Double"];
+ var anInt16 = userControl.Resources["An_Int16"];
+ var anInt32 = userControl.Resources["An_Int32"];
+ var padding = userControl.Resources["PreferredPadding"];
+
+ // Value types shouldn't get source info
+ var foobarStringSourceInfo = XamlSourceInfo.GetXamlSourceInfo(foobarString!);
+ Assert.Null(foobarStringSourceInfo);
+
+ var aDoubleSourceInfo = XamlSourceInfo.GetXamlSourceInfo(aDouble!);
+ Assert.Null(aDoubleSourceInfo);
+
+ var anInt16SourceInfo = XamlSourceInfo.GetXamlSourceInfo(anInt16!);
+ Assert.Null(anInt16SourceInfo);
+
+ var anInt32SourceInfo = XamlSourceInfo.GetXamlSourceInfo(anInt32!);
+ Assert.Null(anInt32SourceInfo);
+
+ var paddingSourceInfo = XamlSourceInfo.GetXamlSourceInfo(padding!);
+ Assert.Null(paddingSourceInfo);
+ }
+
+ [Fact]
+ public void ResourceDictionary_Set_Resource_Source_Info()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+ foobar
+ 123.3
+ 123
+ 37434323
+ 10,20,10,0
+ http://avaloniaui.net
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var resources = userControl.Resources;
+
+ var foobarStringSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "text");
+ Assert.NotNull(foobarStringSourceInfo);
+
+ var aDoubleSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "A_Double");
+ Assert.NotNull(aDoubleSourceInfo);
+
+ var anInt16SourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "An_Int16");
+ Assert.NotNull(anInt16SourceInfo);
+
+ var anInt32SourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "An_Int32");
+ Assert.NotNull(anInt32SourceInfo);
+
+ var paddingSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "PreferredPadding");
+ Assert.NotNull(paddingSourceInfo);
+
+ var homepageSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "homepage");
+ Assert.NotNull(homepageSourceInfo);
+
+ var myBrushSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "MyBrush");
+ Assert.NotNull(myBrushSourceInfo);
+ }
+
+ [Fact]
+ public void ResourceDictionary_Set_Resource_Source_Info_With_Nested_Dictionaries()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+ foobar
+ 123.3
+ 123
+ 37434323
+
+
+ 10,20,10,0
+ http://avaloniaui.net
+
+
+
+
+
+
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var resources = userControl.Resources;
+ var innerResources = (IResourceDictionary)resources.MergedDictionaries[0];
+ var themeResources = (IResourceDictionary)resources.ThemeDictionaries[ThemeVariant.Light];
+
+ // Outer define source info
+ var foobarStringSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "text");
+ Assert.NotNull(foobarStringSourceInfo);
+
+ var aDoubleSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "A_Double");
+ Assert.NotNull(aDoubleSourceInfo);
+
+ var anInt16SourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "An_Int16");
+ Assert.NotNull(anInt16SourceInfo);
+
+ var anInt32SourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "An_Int32");
+ Assert.NotNull(anInt32SourceInfo);
+
+ // Outer one should not have source info for inner resources
+ var paddingSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "PreferredPadding");
+ Assert.Null(paddingSourceInfo);
+
+ var homepageSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "homepage");
+ Assert.Null(homepageSourceInfo);
+
+ var myBrushSourceInfo = XamlSourceInfo.GetXamlSourceInfo(resources, "MyBrush");
+ Assert.Null(myBrushSourceInfo);
+
+ // Inner defined source info
+ homepageSourceInfo = XamlSourceInfo.GetXamlSourceInfo(innerResources, "homepage");
+ Assert.NotNull(homepageSourceInfo);
+
+ myBrushSourceInfo = XamlSourceInfo.GetXamlSourceInfo(themeResources, "MyBrush");
+ Assert.NotNull(myBrushSourceInfo);
+
+ // Non-value types should have source info themselves
+ var homepage = XamlSourceInfo.GetXamlSourceInfo(innerResources["homepage"]!);
+ Assert.NotNull(homepage);
+
+ var myBrush = XamlSourceInfo.GetXamlSourceInfo(themeResources["MyBrush"]!);
+ Assert.NotNull(myBrush);
+ }
+
+ [Fact]
+ public void Gestures_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var scroll = (ScrollGestureRecognizer)userControl.GestureRecognizers.First();
+ var pull = (PullGestureRecognizer)userControl.GestureRecognizers.Last();
+
+ var scrollSourceInfo = XamlSourceInfo.GetXamlSourceInfo(scroll);
+ Assert.NotNull(scrollSourceInfo);
+
+ var pullSourceInfo = XamlSourceInfo.GetXamlSourceInfo(pull);
+ Assert.NotNull(pullSourceInfo);
+ }
+
+ [Fact]
+ public void Transitions_Get_XamlSourceInfo_Set()
+ {
+ var xaml = new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+
+
+");
+
+ var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml, s_configuration);
+ var width = (DoubleTransition)userControl.Transitions!.First();
+ var height = (DoubleTransition)userControl.Transitions!.Last();
+
+ var widthSourceInfo = XamlSourceInfo.GetXamlSourceInfo(width);
+ Assert.NotNull(widthSourceInfo);
+
+ var heightSourceInfo = XamlSourceInfo.GetXamlSourceInfo(height);
+ Assert.NotNull(heightSourceInfo);
+ }
+ }
+
+ public class SourceInfoTestViewModel
+ {
+ public string? Name { get; set; }
+ }
+}
diff --git a/tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs b/tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs
index 1a8d7d9272..3a48bafaad 100644
--- a/tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs
+++ b/tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs
@@ -83,7 +83,7 @@ public class DirectFbCompositionTests : TestBase
SKBitmap fb = new SKBitmap(200, 200, SKColorType.Rgba8888, SKAlphaType.Premul);
ILockedFramebuffer LockFb() => new LockedFramebuffer(fb.GetAddress(0, 0), new(fb.Width, fb.Height),
- fb.RowBytes, new Vector(96, 96), PixelFormat.Rgba8888, null);
+ fb.RowBytes, new Vector(96, 96), PixelFormat.Rgba8888, AlphaFormat.Premul, null);
bool previousFrameIsRetained = false;
IFramebufferRenderTarget rt = advertised
diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs
index 0f849a0400..54e336a6f4 100644
--- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs
+++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs
@@ -25,10 +25,11 @@ namespace Avalonia.Skia.RenderTests
class Framebuffer : ILockedFramebuffer, IFramebufferPlatformSurface
{
- public Framebuffer(PixelFormat fmt, PixelSize size)
+ public Framebuffer(PixelFormat fmt, AlphaFormat alphaFormat, PixelSize size)
{
Format = fmt;
var bpp = fmt == PixelFormat.Rgb565 ? 2 : 4;
+ AlphaFormat = alphaFormat;
Size = size;
RowBytes = bpp * size.Width;
Address = Marshal.AllocHGlobal(size.Height * RowBytes);
@@ -40,6 +41,8 @@ namespace Avalonia.Skia.RenderTests
public PixelFormat Format { get; }
+ public AlphaFormat AlphaFormat { get; }
+
public PixelSize Size { get; }
public int RowBytes { get; }
@@ -64,7 +67,7 @@ namespace Avalonia.Skia.RenderTests
{
var fmt = new PixelFormat(fmte);
var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt;
- var fb = new Framebuffer(fmt, new PixelSize(80, 80));
+ var fb = new Framebuffer(fmt, AlphaFormat.Premul, new PixelSize(80, 80));
var r = AvaloniaLocator.Current.GetRequiredService();
using(var cpuContext = r.CreateBackendContext(null))
using (var target = cpuContext.CreateRenderTarget(new object[] { fb }))
diff --git a/tests/Avalonia.UnitTests/CompositorTestServices.cs b/tests/Avalonia.UnitTests/CompositorTestServices.cs
index ec481328b7..96b3248ed1 100644
--- a/tests/Avalonia.UnitTests/CompositorTestServices.cs
+++ b/tests/Avalonia.UnitTests/CompositorTestServices.cs
@@ -174,7 +174,7 @@ public class CompositorTestServices : IDisposable
{
var ptr = Marshal.AllocHGlobal(128);
return new LockedFramebuffer(ptr, new PixelSize(1, 1), 4, new Vector(96, 96),
- PixelFormat.Rgba8888, () => Marshal.FreeHGlobal(ptr));
+ PixelFormat.Rgba8888, AlphaFormat.Premul, () => Marshal.FreeHGlobal(ptr));
}
public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock);