Browse Source

Merge branch 'master' into poker/wrappanel-enhance

pull/20549/head
Poker 1 week ago
committed by GitHub
parent
commit
913babaaae
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 264
      api/Avalonia.nupkg.xml
  2. 3
      packages/Avalonia/AvaloniaBuildTasks.targets
  3. 5
      packages/Avalonia/AvaloniaRules.Project.xml
  4. 7
      samples/TextTestApp/InteractiveLineControl.cs
  5. 7
      src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
  6. 6
      src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs
  7. 22
      src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
  8. 7
      src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs
  9. 37
      src/Avalonia.Base/Media/FormattedText.cs
  10. 14
      src/Avalonia.Base/Media/Imaging/Bitmap.cs
  11. 4
      src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
  12. 23
      src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs
  13. 4
      src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
  14. 4
      src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
  15. 4
      src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs
  16. 10
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  17. 51
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  18. 11
      src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs
  19. 19
      src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
  20. 5
      src/Avalonia.Base/Platform/ILockedFramebuffer.cs
  21. 11
      src/Avalonia.Base/Platform/IReadableBitmapImpl.cs
  22. 4
      src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
  23. 4
      src/Avalonia.Base/Platform/LockedFramebuffer.cs
  24. 14
      src/Avalonia.Base/Platform/RetainedFramebuffer.cs
  25. 245
      src/Avalonia.Base/Utilities/StringTokenizer.cs
  26. 3
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  27. 4
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  28. 8
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  29. 16
      src/Avalonia.Controls/Design.cs
  30. 10
      src/Avalonia.Controls/Documents/Inline.cs
  31. 37
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  32. 6
      src/Avalonia.Controls/Primitives/TextSearch.cs
  33. 1
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs
  34. 15
      src/Avalonia.Controls/SelectableTextBlock.cs
  35. 4
      src/Avalonia.Controls/TextBlock.cs
  36. 1
      src/Avalonia.Native/DeferredFramebuffer.cs
  37. 2
      src/Avalonia.X11/X11CursorFactory.cs
  38. 2
      src/Avalonia.X11/X11FramebufferSurface.cs
  39. 2
      src/Avalonia.X11/X11IconLoader.cs
  40. 4
      src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs
  41. 111
      src/Browser/Avalonia.Browser/Rendering/RenderWorker.cs
  42. 1
      src/Headless/Avalonia.Headless/Avalonia.Headless.csproj
  43. 6
      src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  44. 4
      src/Headless/Avalonia.Headless/HeadlessUnitTestSession.cs
  45. 1
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  46. 11
      src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
  47. 7
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
  48. 15
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  49. 33
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
  50. 69
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAddSourceInfoTransformer.cs
  51. 191
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs
  52. 343
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlResourceTransformer.cs
  53. 12
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  54. 26
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlAstNewClrObjectHelper.cs
  55. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  56. 151
      src/Markup/Avalonia.Markup.Xaml/Diagnostics/XamlSourceInfo.cs
  57. 6
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs
  58. 5
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs
  59. 2
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  60. 6
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  61. 2
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  62. 2
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  63. 14
      src/Windows/Avalonia.Win32/Interop/Win32Icon.cs
  64. 81
      tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs
  65. 10
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs
  66. 11
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
  67. 4
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
  68. 583
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlSourceInfoTests.cs
  69. 2
      tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs
  70. 7
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  71. 2
      tests/Avalonia.UnitTests/CompositorTestServices.cs

264
api/Avalonia.nupkg.xml

@ -19,6 +19,18 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Platform.IReadableBitmapWithAlphaImpl</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Utilities.StringTokenizer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0001</DiagnosticId> <DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.IScrollable</Target> <Target>T:Avalonia.Controls.Primitives.IScrollable</Target>
@ -49,6 +61,18 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Platform.IReadableBitmapWithAlphaImpl</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Utilities.StringTokenizer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0001</DiagnosticId> <DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.IScrollable</Target> <Target>T:Avalonia.Controls.Primitives.IScrollable</Target>
@ -73,6 +97,24 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.KeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.TextInput.TextInputMethodClient.ShowInputPanel</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.DrawingImage.get_Viewbox</Target> <Target>M:Avalonia.Media.DrawingImage.get_Viewbox</Target>
@ -97,6 +139,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.Imaging.Bitmap.CopyPixels(Avalonia.Platform.ILockedFramebuffer,Avalonia.Platform.AlphaFormat)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target> <Target>M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target>
@ -121,6 +169,48 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.TextFormatting.TextCollapsingProperties.CreateCollapsedRuns(Avalonia.Media.TextFormatting.TextLine,System.Int32,Avalonia.Media.FlowDirection,Avalonia.Media.TextFormatting.TextRun)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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}})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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}})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.GlyphTypeface,System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)</Target> <Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)</Target>
@ -157,6 +247,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.LockedFramebuffer.#ctor(System.IntPtr,Avalonia.PixelSize,System.Int32,Avalonia.Vector,Avalonia.Platform.PixelFormat,System.Action)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target> <Target>M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target>
@ -181,6 +277,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.Documents.Inline.TextDecorationsProperty</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.TextBlock.LetterSpacingProperty</Target> <Target>F:Avalonia.Controls.TextBlock.LetterSpacingProperty</Target>
@ -223,6 +325,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target> <Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
@ -259,6 +367,18 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.TextSearch.GetText(Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.TextSearch.SetText(Avalonia.Controls.Control,System.String)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)</Target> <Target>M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)</Target>
@ -343,6 +463,24 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.KeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Input.TextInput.TextInputMethodClient.ShowInputPanel</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.DrawingImage.get_Viewbox</Target> <Target>M:Avalonia.Media.DrawingImage.get_Viewbox</Target>
@ -367,6 +505,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.Imaging.Bitmap.CopyPixels(Avalonia.Platform.ILockedFramebuffer,Avalonia.Platform.AlphaFormat)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target> <Target>M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target>
@ -391,6 +535,48 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.TextFormatting.TextCollapsingProperties.CreateCollapsedRuns(Avalonia.Media.TextFormatting.TextLine,System.Int32,Avalonia.Media.FlowDirection,Avalonia.Media.TextFormatting.TextRun)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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}})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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}})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>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)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.TextFormatting.TextShaperOptions.#ctor(Avalonia.Media.GlyphTypeface,System.Double,System.SByte,System.Globalization.CultureInfo,System.Double,System.Double)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)</Target> <Target>M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect)</Target>
@ -427,6 +613,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.LockedFramebuffer.#ctor(System.IntPtr,Avalonia.PixelSize,System.Int32,Avalonia.Vector,Avalonia.Platform.PixelFormat,System.Action)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target> <Target>M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target>
@ -451,6 +643,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.Documents.Inline.TextDecorationsProperty</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.TextBlock.LetterSpacingProperty</Target> <Target>F:Avalonia.Controls.TextBlock.LetterSpacingProperty</Target>
@ -493,6 +691,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target> <Target>M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control})</Target>
@ -529,6 +733,18 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.TextSearch.GetText(Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.TextSearch.SetText(Avalonia.Controls.Control,System.String)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)</Target> <Target>M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)</Target>
@ -619,6 +835,12 @@
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left> <Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right> <Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers,System.Nullable{Avalonia.Input.KeyDeviceType})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.IDrawingContextImpl.PopTextOptions</Target> <Target>M:Avalonia.Platform.IDrawingContextImpl.PopTextOptions</Target>
@ -667,6 +889,18 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.IReadableBitmapImpl.AlphaFormat</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target> <Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target>
@ -727,6 +961,12 @@
<Left>baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Left> <Left>baseline/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Left>
<Right>current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Right> <Right>current/Avalonia/lib/net6.0/Avalonia.OpenGL.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers,System.Nullable{Avalonia.Input.KeyDeviceType})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target> <Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target>
@ -811,6 +1051,18 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.IReadableBitmapImpl.AlphaFormat</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target> <Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
@ -895,12 +1147,24 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Platform.IWriteableBitmapImpl</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0008</DiagnosticId> <DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Media.StreamGeometryContext</Target> <Target>T:Avalonia.Media.StreamGeometryContext</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Platform.IWriteableBitmapImpl</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0009</DiagnosticId> <DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Platform.Screen</Target> <Target>T:Avalonia.Platform.Screen</Target>

3
packages/Avalonia/AvaloniaBuildTasks.targets

@ -134,6 +134,8 @@
<AvaloniaXamlIlVerifyIl Condition="'$(AvaloniaXamlIlVerifyIl)' == ''">false</AvaloniaXamlIlVerifyIl> <AvaloniaXamlIlVerifyIl Condition="'$(AvaloniaXamlIlVerifyIl)' == ''">false</AvaloniaXamlIlVerifyIl>
<AvaloniaXamlIlDebuggerLaunch Condition="'$(AvaloniaXamlIlDebuggerLaunch)' == ''">false</AvaloniaXamlIlDebuggerLaunch> <AvaloniaXamlIlDebuggerLaunch Condition="'$(AvaloniaXamlIlDebuggerLaunch)' == ''">false</AvaloniaXamlIlDebuggerLaunch>
<AvaloniaXamlVerboseExceptions Condition="'$(AvaloniaXamlVerboseExceptions)' == ''">false</AvaloniaXamlVerboseExceptions> <AvaloniaXamlVerboseExceptions Condition="'$(AvaloniaXamlVerboseExceptions)' == ''">false</AvaloniaXamlVerboseExceptions>
<AvaloniaXamlCreateSourceInfo Condition="'$(AvaloniaXamlCreateSourceInfo)' == '' AND '$(Configuration)' == 'Debug'">true</AvaloniaXamlCreateSourceInfo>
<AvaloniaXamlCreateSourceInfo Condition="'$(AvaloniaXamlCreateSourceInfo)' == '' AND '$(Configuration)' != 'Debug'">false</AvaloniaXamlCreateSourceInfo>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -162,6 +164,7 @@
DelaySign="$(DelaySign)" DelaySign="$(DelaySign)"
SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)" SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)"
DebuggerLaunch="$(AvaloniaXamlIlDebuggerLaunch)" DebuggerLaunch="$(AvaloniaXamlIlDebuggerLaunch)"
CreateSourceInfo="$(AvaloniaXamlCreateSourceInfo)"
DefaultCompileBindings="$(AvaloniaUseCompiledBindingsByDefault)" DefaultCompileBindings="$(AvaloniaUseCompiledBindingsByDefault)"
VerboseExceptions="$(AvaloniaXamlVerboseExceptions)" VerboseExceptions="$(AvaloniaXamlVerboseExceptions)"
AnalyzerConfigFiles="@(EditorConfigFiles)"/> AnalyzerConfigFiles="@(EditorConfigFiles)"/>

5
packages/Avalonia/AvaloniaRules.Project.xml

@ -31,6 +31,11 @@
Description="Allow debug XAML compilation" Description="Allow debug XAML compilation"
Category="Debug" /> Category="Debug" />
<BoolProperty Name="AvaloniaXamlCreateSourceInfo"
DisplayName="Generate XAML Source Info"
Description="When enabled, the XAML compiler embeds SourceInfo metadata (file path, line, and column) into generated code. This allows tools and debuggers to locate the original .axaml source position of a selected element at runtime or in the designer."
Category="Debug" />
<BoolProperty Name="AvaloniaXamlVerboseExceptions" <BoolProperty Name="AvaloniaXamlVerboseExceptions"
DisplayName="Report verbose internal exceptions with stack traces" DisplayName="Report verbose internal exceptions with stack traces"
Description="Also includes inner exceptions" Description="Also includes inner exceptions"

7
samples/TextTestApp/InteractiveLineControl.cs

@ -255,12 +255,15 @@ namespace TextTestApp
private GenericTextRunProperties CreateTextRunProperties() private GenericTextRunProperties CreateTextRunProperties()
{ {
Typeface typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch); Typeface typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
return new GenericTextRunProperties(typeface, FontFeatures, FontSize, return new GenericTextRunProperties(
typeface,
FontSize,
textDecorations: null, textDecorations: null,
foregroundBrush: Brushes.Black, foregroundBrush: Brushes.Black,
backgroundBrush: null, backgroundBrush: null,
baselineAlignment: BaselineAlignment.Baseline, baselineAlignment: BaselineAlignment.Baseline,
cultureInfo: null); cultureInfo: null,
fontFeatures: FontFeatures);
} }
// TextParagraphProperties // TextParagraphProperties

7
src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs

@ -25,10 +25,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
Size = new PixelSize(rc.right, rc.bottom); Size = new PixelSize(rc.right, rc.bottom);
ANativeWindow_lock(_window, &buffer, &rc); ANativeWindow_lock(_window, &buffer, &rc);
Format = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565 (Format, AlphaFormat, RowBytes) = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565 ?
? PixelFormat.Rgb565 : PixelFormat.Rgba8888; (PixelFormat.Rgb565, AlphaFormat.Opaque, buffer.stride * 2) :
(PixelFormat.Rgba8888, AlphaFormat.Premul, buffer.stride * 4);
RowBytes = buffer.stride * (Format == PixelFormat.Rgb565 ? 2 : 4);
Address = buffer.bits; Address = buffer.bits;
Dpi = new Vector(96, 96) * scaling; Dpi = new Vector(96, 96) * scaling;
@ -46,6 +46,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public int RowBytes { get; } public int RowBytes { get; }
public Vector Dpi { get; } public Vector Dpi { get; }
public PixelFormat Format { get; } public PixelFormat Format { get; }
public AlphaFormat AlphaFormat { get; }
[DllImport("android")] [DllImport("android")]
internal static extern IntPtr ANativeWindow_fromSurface(IntPtr jniEnv, IntPtr handle); internal static extern IntPtr ANativeWindow_fromSurface(IntPtr jniEnv, IntPtr handle);

6
src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs

@ -24,9 +24,11 @@ namespace Avalonia.Input
/// <param name="element">The current element.</param> /// <param name="element">The current element.</param>
/// <param name="direction">The direction to move.</param> /// <param name="direction">The direction to move.</param>
/// <param name="keyModifiers">Any key modifiers active at the time of focus.</param> /// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
void Move( /// <param name="deviceType">The device type used to move the focus.</param>
bool Move(
IInputElement element, IInputElement element,
NavigationDirection direction, NavigationDirection direction,
KeyModifiers keyModifiers = KeyModifiers.None); KeyModifiers keyModifiers = KeyModifiers.None,
KeyDeviceType? deviceType = null);
} }
} }

22
src/Avalonia.Base/Input/KeyboardNavigationHandler.cs

@ -98,22 +98,12 @@ namespace Avalonia.Input
return result; return result;
} }
/// <summary> /// <inheritdoc />
/// Moves the focus in the specified direction. public bool Move(
/// </summary>
/// <param name="element">The current element.</param>
/// <param name="direction">The direction to move.</param>
/// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
public void Move(
IInputElement? element, IInputElement? element,
NavigationDirection direction, NavigationDirection direction,
KeyModifiers keyModifiers = KeyModifiers.None) KeyModifiers keyModifiers = KeyModifiers.None,
{ KeyDeviceType? deviceType = null)
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)
{ {
var next = GetNextPrivate(element, _owner, direction, deviceType); var next = GetNextPrivate(element, _owner, direction, deviceType);
@ -140,7 +130,7 @@ namespace Avalonia.Input
var current = FocusManager.GetFocusManager(e.Source as IInputElement)?.GetFocusedElement(); var current = FocusManager.GetFocusManager(e.Source as IInputElement)?.GetFocusedElement();
var direction = (e.KeyModifiers & KeyModifiers.Shift) == 0 ? var direction = (e.KeyModifiers & KeyModifiers.Shift) == 0 ?
NavigationDirection.Next : NavigationDirection.Previous; 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) 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, Key.Down => NavigationDirection.Down,
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
e.Handled = MovePrivate(current, direction, e.KeyModifiers, e.KeyDeviceType); e.Handled = Move(current, direction, e.KeyModifiers, e.KeyDeviceType);
} }
} }

7
src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs

@ -82,13 +82,6 @@ namespace Avalonia.Input.TextInput
{ {
SetPreeditText(preeditText); SetPreeditText(preeditText);
} }
//TODO12: remove
[Obsolete]
public virtual void ShowInputPanel()
{
RaiseInputPaneActivationRequested();
}
protected virtual void RaiseTextViewVisualChanged() protected virtual void RaiseTextViewVisualChanged()
{ {

37
src/Avalonia.Base/Media/FormattedText.cs

@ -184,13 +184,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
runProps.Typeface, runProps.Typeface,
runProps.FontFeatures,
runProps.FontRenderingEmSize, runProps.FontRenderingEmSize,
runProps.TextDecorations, runProps.TextDecorations,
foregroundBrush, foregroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
runProps.CultureInfo runProps.CultureInfo,
runProps.FontFeatures
); );
#pragma warning restore 6506 #pragma warning restore 6506
@ -240,13 +240,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
runProps.Typeface, runProps.Typeface,
fontFeatures,
runProps.FontRenderingEmSize, runProps.FontRenderingEmSize,
runProps.TextDecorations, runProps.TextDecorations,
runProps.ForegroundBrush, runProps.ForegroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
runProps.CultureInfo runProps.CultureInfo,
fontFeatures
); );
#pragma warning restore 6506 #pragma warning restore 6506
@ -328,14 +328,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
new Typeface(fontFamily, oldTypeface.Style, oldTypeface.Weight), new Typeface(fontFamily, oldTypeface.Style, oldTypeface.Weight),
runProps.FontFeatures,
runProps.FontRenderingEmSize, runProps.FontRenderingEmSize,
runProps.TextDecorations, runProps.TextDecorations,
runProps.ForegroundBrush, runProps.ForegroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
runProps.CultureInfo runProps.CultureInfo,
); runProps.FontFeatures);
#pragma warning restore 6506 #pragma warning restore 6506
_latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
@ -388,13 +387,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
runProps.Typeface, runProps.Typeface,
runProps.FontFeatures,
emSize, emSize,
runProps.TextDecorations, runProps.TextDecorations,
runProps.ForegroundBrush, runProps.ForegroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
runProps.CultureInfo runProps.CultureInfo,
runProps.FontFeatures
); );
_latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
@ -451,13 +450,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
runProps.Typeface, runProps.Typeface,
runProps.FontFeatures,
runProps.FontRenderingEmSize, runProps.FontRenderingEmSize,
runProps.TextDecorations, runProps.TextDecorations,
runProps.ForegroundBrush, runProps.ForegroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
culture culture,
runProps.FontFeatures
); );
#pragma warning restore 6506 #pragma warning restore 6506
@ -511,13 +510,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
new Typeface(oldTypeface.FontFamily, oldTypeface.Style, weight), new Typeface(oldTypeface.FontFamily, oldTypeface.Style, weight),
runProps.FontFeatures,
runProps.FontRenderingEmSize, runProps.FontRenderingEmSize,
runProps.TextDecorations, runProps.TextDecorations,
runProps.ForegroundBrush, runProps.ForegroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
runProps.CultureInfo runProps.CultureInfo,
runProps.FontFeatures
); );
#pragma warning restore 6506 #pragma warning restore 6506
_latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, newProps, formatRider.SpanPosition); _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, newProps, formatRider.SpanPosition);
@ -568,13 +567,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
new Typeface(oldTypeface.FontFamily, style, oldTypeface.Weight), new Typeface(oldTypeface.FontFamily, style, oldTypeface.Weight),
runProps.FontFeatures,
runProps.FontRenderingEmSize, runProps.FontRenderingEmSize,
runProps.TextDecorations, runProps.TextDecorations,
runProps.ForegroundBrush, runProps.ForegroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
runProps.CultureInfo runProps.CultureInfo,
runProps.FontFeatures
); );
#pragma warning restore 6506 #pragma warning restore 6506
@ -625,13 +624,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
typeface, typeface,
runProps.FontFeatures,
runProps.FontRenderingEmSize, runProps.FontRenderingEmSize,
runProps.TextDecorations, runProps.TextDecorations,
runProps.ForegroundBrush, runProps.ForegroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
runProps.CultureInfo runProps.CultureInfo,
runProps.FontFeatures
); );
#pragma warning restore 6506 #pragma warning restore 6506
@ -683,13 +682,13 @@ namespace Avalonia.Media
var newProps = new GenericTextRunProperties( var newProps = new GenericTextRunProperties(
runProps.Typeface, runProps.Typeface,
runProps.FontFeatures,
runProps.FontRenderingEmSize, runProps.FontRenderingEmSize,
textDecorations, textDecorations,
runProps.ForegroundBrush, runProps.ForegroundBrush,
runProps.BackgroundBrush, runProps.BackgroundBrush,
runProps.BaselineAlignment, runProps.BaselineAlignment,
runProps.CultureInfo runProps.CultureInfo,
runProps.FontFeatures
); );
#pragma warning restore 6506 #pragma warning restore 6506

14
src/Avalonia.Base/Media/Imaging/Bitmap.cs

@ -1,5 +1,4 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Avalonia.Platform; using Avalonia.Platform;
@ -177,7 +176,7 @@ namespace Avalonia.Media.Imaging
public virtual PixelFormat? Format => (PlatformImpl.Item as IReadableBitmapImpl)?.Format; 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, private protected unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride,
ILockedFramebuffer fb) 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. /// Copies pixels to the target buffer and transcodes the pixel and alpha format if needed.
/// </summary> /// </summary>
/// <param name="buffer">The target buffer.</param> /// <param name="buffer">The target buffer.</param>
/// <param name="alphaFormat">The alpha format.</param>
/// <exception cref="NotSupportedException"></exception> /// <exception cref="NotSupportedException"></exception>
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"); 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()) using (var fb = readable.Lock())
{ {
@ -255,11 +253,11 @@ namespace Avalonia.Media.Imaging
fb.Size, fb.Size,
fb.RowBytes, fb.RowBytes,
fb.Format, fb.Format,
readable.AlphaFormat.Value, fb.AlphaFormat,
buffer.Address, buffer.Address,
buffer.RowBytes, buffer.RowBytes,
buffer.Format, buffer.Format,
alphaFormat); buffer.AlphaFormat);
} }
} }
else else

4
src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs

@ -71,10 +71,10 @@ namespace Avalonia.Media.Imaging
return new LockedFramebuffer(_pixelFormatMemory.Address, _pixelFormatMemory.Size, return new LockedFramebuffer(_pixelFormatMemory.Address, _pixelFormatMemory.Size,
_pixelFormatMemory.RowBytes, _pixelFormatMemory.RowBytes,
Dpi, _pixelFormatMemory.Format, () => Dpi, _pixelFormatMemory.Format, _pixelFormatMemory.AlphaFormat, () =>
{ {
using var inner = ((IWriteableBitmapImpl)PlatformImpl.Item).Lock(); using var inner = ((IWriteableBitmapImpl)PlatformImpl.Item).Lock();
_pixelFormatMemory.CopyToRgba(Platform.AlphaFormat.Unpremul, inner.Address, inner.RowBytes); _pixelFormatMemory.CopyToRgba(inner.AlphaFormat, inner.Address, inner.RowBytes);
}); });
} }

23
src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs

@ -1,6 +1,4 @@
using System; using System.Globalization;
using System.Collections.Generic;
using System.Globalization;
namespace Avalonia.Media.TextFormatting namespace Avalonia.Media.TextFormatting
{ {
@ -9,28 +7,17 @@ namespace Avalonia.Media.TextFormatting
/// </summary> /// </summary>
public class GenericTextRunProperties : TextRunProperties 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( public GenericTextRunProperties(
Typeface typeface, Typeface typeface,
FontFeatureCollection? fontFeatures,
double fontRenderingEmSize = DefaultFontRenderingEmSize, double fontRenderingEmSize = DefaultFontRenderingEmSize,
TextDecorationCollection? textDecorations = null, TextDecorationCollection? textDecorations = null,
IBrush? foregroundBrush = null, IBrush? foregroundBrush = null,
IBrush? backgroundBrush = null, IBrush? backgroundBrush = null,
BaselineAlignment baselineAlignment = BaselineAlignment.Baseline, BaselineAlignment baselineAlignment = BaselineAlignment.Baseline,
CultureInfo? cultureInfo = null) CultureInfo? cultureInfo = null,
FontFeatureCollection? fontFeatures = null)
{ {
Typeface = typeface; Typeface = typeface;
FontRenderingEmSize = fontRenderingEmSize; FontRenderingEmSize = fontRenderingEmSize;

4
src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs

@ -26,16 +26,14 @@
/// <param name="textLine">Text line to collapse.</param> /// <param name="textLine">Text line to collapse.</param>
public abstract TextRun[]? Collapse(TextLine textLine); public abstract TextRun[]? Collapse(TextLine textLine);
// TODO12: Remove the flowDirection parameter
/// <summary> /// <summary>
/// Creates a list of runs for given collapsed length which includes specified symbol at the end. /// Creates a list of runs for given collapsed length which includes specified symbol at the end.
/// </summary> /// </summary>
/// <param name="textLine">The text line.</param> /// <param name="textLine">The text line.</param>
/// <param name="collapsedLength">The collapsed length.</param> /// <param name="collapsedLength">The collapsed length.</param>
/// <param name="flowDirection">The flow direction.</param>
/// <param name="shapedSymbol">The symbol.</param> /// <param name="shapedSymbol">The symbol.</param>
/// <returns>List of remaining runs.</returns> /// <returns>List of remaining runs.</returns>
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) if (collapsedLength <= 0)
{ {

4
src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs

@ -71,7 +71,7 @@ namespace Avalonia.Media.TextFormatting
collapsedLength += measuredLength; collapsedLength += measuredLength;
return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, properties.FlowDirection, shapedSymbol); return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, shapedSymbol);
} }
availableWidth -= textRunWidth; availableWidth -= textRunWidth;
@ -84,7 +84,7 @@ namespace Avalonia.Media.TextFormatting
//The whole run needs to fit into available space //The whole run needs to fit into available space
if (drawableRun.Size.Width > availableWidth) if (drawableRun.Size.Width > availableWidth)
{ {
return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, properties.FlowDirection, shapedSymbol); return TextCollapsingProperties.CreateCollapsedRuns(textLine, collapsedLength, shapedSymbol);
} }
availableWidth -= drawableRun.Size.Width; availableWidth -= drawableRun.Size.Width;

4
src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs

@ -59,8 +59,8 @@
var cultureInfo = textRun.Properties.CultureInfo; var cultureInfo = textRun.Properties.CultureInfo;
var shaperOptions = new TextShaperOptions(glyphTypeface, textRun.Properties.FontFeatures, var shaperOptions = new TextShaperOptions(glyphTypeface, fontRenderingEmSize,
fontRenderingEmSize, (sbyte)flowDirection, cultureInfo); (sbyte)flowDirection, cultureInfo, 0, 0, textRun.Properties.FontFeatures);
var shapedBuffer = textShaper.ShapeText(textRun.Text, shaperOptions); var shapedBuffer = textShaper.ShapeText(textRun.Text, shaperOptions);

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

@ -290,9 +290,13 @@ namespace Avalonia.Media.TextFormatting
} }
var shaperOptions = new TextShaperOptions( var shaperOptions = new TextShaperOptions(
properties.CachedGlyphTypeface, properties.FontFeatures, properties.CachedGlyphTypeface,
properties.FontRenderingEmSize, shapeableRun.BidiLevel, properties.CultureInfo, properties.FontRenderingEmSize,
paragraphProperties.DefaultIncrementalTab, paragraphProperties.LetterSpacing); shapeableRun.BidiLevel,
properties.CultureInfo,
paragraphProperties.DefaultIncrementalTab,
paragraphProperties.LetterSpacing,
properties.FontFeatures);
ShapeTogether(groupedRuns, text, shaperOptions, textShaper, shapedRuns); ShapeTogether(groupedRuns, text, shaperOptions, textShaper, shapedRuns);

51
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

@ -17,7 +17,6 @@ namespace Avalonia.Media.TextFormatting
private int _textSourceLength; private int _textSourceLength;
// TODO12: Remove in 12.0.0 and make fontFeatures parameter in main ctor optional
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TextLayout" /> class. /// Initializes a new instance of the <see cref="TextLayout" /> class.
/// </summary> /// </summary>
@ -35,54 +34,13 @@ namespace Avalonia.Media.TextFormatting
/// <param name="lineHeight">The height of each line of text.</param> /// <param name="lineHeight">The height of each line of text.</param>
/// <param name="letterSpacing">The letter spacing that is applied to rendered glyphs.</param> /// <param name="letterSpacing">The letter spacing that is applied to rendered glyphs.</param>
/// <param name="maxLines">The maximum number of text lines.</param> /// <param name="maxLines">The maximum number of text lines.</param>
/// <param name="textStyleOverrides">The text style overrides.</param>
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<ValueSpan<TextRunProperties>>? 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
/// <summary>
/// Initializes a new instance of the <see cref="TextLayout" /> class.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="typeface">The typeface.</param>
/// <param name="fontSize">Size of the font.</param>
/// <param name="foreground">The foreground.</param>
/// <param name="textAlignment">The text alignment.</param>
/// <param name="textWrapping">The text wrapping.</param>
/// <param name="textTrimming">The text trimming.</param>
/// <param name="textDecorations">The text decorations.</param>
/// <param name="flowDirection">The text flow direction.</param>
/// <param name="maxWidth">The maximum width.</param>
/// <param name="maxHeight">The maximum height.</param>
/// <param name="lineHeight">The height of each line of text.</param>
/// <param name="letterSpacing">The letter spacing that is applied to rendered glyphs.</param>
/// <param name="maxLines">The maximum number of text lines.</param>
/// <param name="textStyleOverrides">The text style overrides.</param>
/// <param name="fontFeatures">Optional list of turned on/off features.</param> /// <param name="fontFeatures">Optional list of turned on/off features.</param>
/// <param name="textStyleOverrides">The text style overrides.</param>
public TextLayout( public TextLayout(
string? text, string? text,
Typeface typeface, Typeface typeface,
FontFeatureCollection? fontFeatures, double fontSize = GenericTextRunProperties.DefaultFontRenderingEmSize,
double fontSize, IBrush? foreground = null,
IBrush? foreground,
TextAlignment textAlignment = TextAlignment.Left, TextAlignment textAlignment = TextAlignment.Left,
TextWrapping textWrapping = TextWrapping.NoWrap, TextWrapping textWrapping = TextWrapping.NoWrap,
TextTrimming? textTrimming = null, TextTrimming? textTrimming = null,
@ -93,6 +51,7 @@ namespace Avalonia.Media.TextFormatting
double lineHeight = double.NaN, double lineHeight = double.NaN,
double letterSpacing = 0, double letterSpacing = 0,
int maxLines = 0, int maxLines = 0,
FontFeatureCollection? fontFeatures = null,
IReadOnlyList<ValueSpan<TextRunProperties>>? textStyleOverrides = null) IReadOnlyList<ValueSpan<TextRunProperties>>? textStyleOverrides = null)
{ {
_paragraphProperties = _paragraphProperties =
@ -534,7 +493,7 @@ namespace Avalonia.Media.TextFormatting
TextDecorationCollection? textDecorations, FlowDirection flowDirection, double lineHeight, TextDecorationCollection? textDecorations, FlowDirection flowDirection, double lineHeight,
double letterSpacing, FontFeatureCollection? features) 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, return new GenericTextParagraphProperties(flowDirection, textAlignment, true, false,
textRunStyle, textWrapping, lineHeight, 0, letterSpacing); textRunStyle, textWrapping, lineHeight, 0, letterSpacing);

11
src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs

@ -107,8 +107,15 @@ namespace Avalonia.Media.TextFormatting
if (this is GenericTextRunProperties other && other.Typeface == typeface) if (this is GenericTextRunProperties other && other.Typeface == typeface)
return this; return this;
return new GenericTextRunProperties(typeface, FontFeatures, FontRenderingEmSize, return new GenericTextRunProperties(
TextDecorations, ForegroundBrush, BackgroundBrush, BaselineAlignment); typeface,
FontRenderingEmSize,
TextDecorations,
ForegroundBrush,
BackgroundBrush,
BaselineAlignment,
CultureInfo,
FontFeatures);
} }
} }
} }

19
src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs

@ -8,27 +8,14 @@ namespace Avalonia.Media.TextFormatting
/// </summary> /// </summary>
public readonly record struct TextShaperOptions 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( public TextShaperOptions(
GlyphTypeface typeface, GlyphTypeface typeface,
IReadOnlyList<FontFeature>? fontFeatures, double fontRenderingEmSize = GenericTextRunProperties.DefaultFontRenderingEmSize,
double fontRenderingEmSize = 12,
sbyte bidiLevel = 0, sbyte bidiLevel = 0,
CultureInfo? culture = null, CultureInfo? culture = null,
double incrementalTabWidth = 0, double incrementalTabWidth = 0,
double letterSpacing = 0) double letterSpacing = 0,
IReadOnlyList<FontFeature>? fontFeatures = null)
{ {
GlyphTypeface = typeface; GlyphTypeface = typeface;
FontRenderingEmSize = fontRenderingEmSize; FontRenderingEmSize = fontRenderingEmSize;

5
src/Avalonia.Base/Platform/ILockedFramebuffer.cs

@ -29,6 +29,9 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
PixelFormat Format { get; } PixelFormat Format { get; }
//TODO12: Add AlphaFormat /// <summary>
/// Gets the alpha format.
/// </summary>
AlphaFormat AlphaFormat { get; }
} }
} }

11
src/Avalonia.Base/Platform/IReadableBitmapImpl.cs

@ -2,15 +2,10 @@ using Avalonia.Metadata;
namespace Avalonia.Platform; namespace Avalonia.Platform;
public interface IReadableBitmapImpl [PrivateApi]
public interface IReadableBitmapImpl : IBitmapImpl
{ {
PixelFormat? Format { get; } PixelFormat? Format { get; }
ILockedFramebuffer Lock();
}
//TODO12: Remove me once we can change IReadableBitmapImpl
[Unstable]
public interface IReadableBitmapWithAlphaImpl : IReadableBitmapImpl
{
AlphaFormat? AlphaFormat { get; } AlphaFormat? AlphaFormat { get; }
ILockedFramebuffer Lock();
} }

4
src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs

@ -5,8 +5,8 @@ namespace Avalonia.Platform
/// <summary> /// <summary>
/// Defines the platform-specific interface for a <see cref="Avalonia.Media.Imaging.WriteableBitmap"/>. /// Defines the platform-specific interface for a <see cref="Avalonia.Media.Imaging.WriteableBitmap"/>.
/// </summary> /// </summary>
[Unstable] [PrivateApi]
public interface IWriteableBitmapImpl : IBitmapImpl, IReadableBitmapWithAlphaImpl public interface IWriteableBitmapImpl : IBitmapImpl, IReadableBitmapImpl
{ {
} }
} }

4
src/Avalonia.Base/Platform/LockedFramebuffer.cs

@ -7,7 +7,7 @@ namespace Avalonia.Platform
private readonly Action? _onDispose; private readonly Action? _onDispose;
public LockedFramebuffer(IntPtr address, PixelSize size, int rowBytes, Vector dpi, PixelFormat format, public LockedFramebuffer(IntPtr address, PixelSize size, int rowBytes, Vector dpi, PixelFormat format,
Action? onDispose) AlphaFormat alphaFormat, Action? onDispose)
{ {
_onDispose = onDispose; _onDispose = onDispose;
Address = address; Address = address;
@ -15,6 +15,7 @@ namespace Avalonia.Platform
RowBytes = rowBytes; RowBytes = rowBytes;
Dpi = dpi; Dpi = dpi;
Format = format; Format = format;
AlphaFormat = alphaFormat;
} }
public IntPtr Address { get; } public IntPtr Address { get; }
@ -22,6 +23,7 @@ namespace Avalonia.Platform
public int RowBytes { get; } public int RowBytes { get; }
public Vector Dpi { get; } public Vector Dpi { get; }
public PixelFormat Format { get; } public PixelFormat Format { get; }
public AlphaFormat AlphaFormat { get; }
public void Dispose() public void Dispose()
{ {

14
src/Avalonia.Base/Platform/RetainedFramebuffer.cs

@ -1,6 +1,4 @@
using System; using System;
using System.Runtime.InteropServices;
using Avalonia.Metadata;
using Avalonia.Platform.Internal; using Avalonia.Platform.Internal;
namespace Avalonia.Platform; namespace Avalonia.Platform;
@ -10,6 +8,7 @@ internal class RetainedFramebuffer : IDisposable
public PixelSize Size { get; } public PixelSize Size { get; }
public int RowBytes { get; } public int RowBytes { get; }
public PixelFormat Format { get; } public PixelFormat Format { get; }
public AlphaFormat AlphaFormat { get; }
public IntPtr Address => _blob?.Address ?? throw new ObjectDisposedException(nameof(RetainedFramebuffer)); public IntPtr Address => _blob?.Address ?? throw new ObjectDisposedException(nameof(RetainedFramebuffer));
private UnmanagedBlob? _blob; private UnmanagedBlob? _blob;
@ -17,13 +16,13 @@ internal class RetainedFramebuffer : IDisposable
? format ? format
: throw new ArgumentOutOfRangeException(nameof(format)); : throw new ArgumentOutOfRangeException(nameof(format));
public RetainedFramebuffer(PixelSize size, PixelFormat format) : this(size, ValidateKnownFormat(format), public RetainedFramebuffer(PixelSize size, PixelFormat format, AlphaFormat alphaFormat)
format.BitsPerPixel / 8 * size.Width) : 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) if (size.Width <= 0 || size.Height <= 0)
throw new ArgumentOutOfRangeException(nameof(size)); throw new ArgumentOutOfRangeException(nameof(size));
@ -32,6 +31,7 @@ internal class RetainedFramebuffer : IDisposable
Size = size; Size = size;
RowBytes = rowBytes; RowBytes = rowBytes;
Format = format; Format = format;
AlphaFormat = alphaFormat;
_blob = new UnmanagedBlob(RowBytes * size.Height); _blob = new UnmanagedBlob(RowBytes * size.Height);
} }
@ -39,7 +39,7 @@ internal class RetainedFramebuffer : IDisposable
{ {
if (_blob == null) if (_blob == null)
throw new ObjectDisposedException(nameof(RetainedFramebuffer)); 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); blit(this);
GC.KeepAlive(this); GC.KeepAlive(this);
@ -51,4 +51,4 @@ internal class RetainedFramebuffer : IDisposable
_blob?.Dispose(); _blob?.Dispose();
_blob = null; _blob = null;
} }
} }

245
src/Avalonia.Base/Utilities/StringTokenizer.cs

@ -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<char> CurrentTokenSpan => _tokenIndex < 0 ? ReadOnlySpan<char>.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<char> result, char? separator = null)
{
var success = TryReadToken(separator ?? _separator);
result = CurrentTokenSpan;
return success;
}
public ReadOnlySpan<char> 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;
}
}
}

3
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -62,9 +62,6 @@
<Compile Include="../Avalonia.Base/Utilities/IdentifierParser.cs"> <Compile Include="../Avalonia.Base/Utilities/IdentifierParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="../Avalonia.Base/Utilities/StringTokenizer.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Utilities/MathUtilities.cs"> <Compile Include="../Avalonia.Base/Utilities/MathUtilities.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>

4
src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs

@ -32,7 +32,7 @@ namespace Avalonia.Build.Tasks
ProjectDirectory, VerifyIl, DefaultCompileBindings, outputImportance, ProjectDirectory, VerifyIl, DefaultCompileBindings, outputImportance,
new XamlCompilerDiagnosticsFilter(AnalyzerConfigFiles), new XamlCompilerDiagnosticsFilter(AnalyzerConfigFiles),
(SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null, (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null,
SkipXamlCompilation, DebuggerLaunch, VerboseExceptions); SkipXamlCompilation, DebuggerLaunch, VerboseExceptions, CreateSourceInfo);
if (res.Success && !res.WrittenFile) if (res.Success && !res.WrittenFile)
{ {
@ -99,6 +99,8 @@ namespace Avalonia.Build.Tasks
public bool DebuggerLaunch { get; set; } public bool DebuggerLaunch { get; set; }
public bool CreateSourceInfo { get; set; }
public bool VerboseExceptions { get; set; } public bool VerboseExceptions { get; set; }
public ITaskItem[] AnalyzerConfigFiles { get; set; } public ITaskItem[] AnalyzerConfigFiles { get; set; }

8
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -52,7 +52,7 @@ namespace Avalonia.Build.Tasks
string[] references, string projectDirectory, string[] references, string projectDirectory,
bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance,
XamlCompilerDiagnosticsFilter diagnosticsFilter, string strongNameKey, XamlCompilerDiagnosticsFilter diagnosticsFilter, string strongNameKey,
bool skipXamlCompilation, bool debuggerLaunch, bool verboseExceptions) bool skipXamlCompilation, bool debuggerLaunch, bool verboseExceptions, bool createSourceInfo)
{ {
try try
{ {
@ -67,7 +67,7 @@ namespace Avalonia.Build.Tasks
var compileRes = CompileCore( var compileRes = CompileCore(
engine, typeSystem, projectDirectory, verifyIl, engine, typeSystem, projectDirectory, verifyIl,
defaultCompileBindings, logImportance, diagnosticsFilter, defaultCompileBindings, logImportance, diagnosticsFilter,
debuggerLaunch, verboseExceptions); debuggerLaunch, verboseExceptions, createSourceInfo);
if (compileRes == null) if (compileRes == null)
return new CompileResult(true); return new CompileResult(true);
if (compileRes == false) if (compileRes == false)
@ -107,7 +107,8 @@ namespace Avalonia.Build.Tasks
MessageImportance logImportance, MessageImportance logImportance,
XamlCompilerDiagnosticsFilter diagnosticsFilter, XamlCompilerDiagnosticsFilter diagnosticsFilter,
bool debuggerLaunch, bool debuggerLaunch,
bool verboseExceptions) bool verboseExceptions,
bool createSourceInfo)
{ {
if (debuggerLaunch) if (debuggerLaunch)
{ {
@ -210,6 +211,7 @@ namespace Avalonia.Build.Tasks
{ {
EnableIlVerification = verifyIl, EnableIlVerification = verifyIl,
DefaultCompileBindings = defaultCompileBindings, DefaultCompileBindings = defaultCompileBindings,
CreateSourceInfo = createSourceInfo,
DynamicSetterContainerProvider = new DefaultXamlDynamicSetterContainerProvider(dynamicSettersBuilder) DynamicSetterContainerProvider = new DefaultXamlDynamicSetterContainerProvider(dynamicSettersBuilder)
}; };

16
src/Avalonia.Controls/Design.cs

@ -127,22 +127,6 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public static readonly AttachedProperty<Control?> PreviewWithProperty = AvaloniaProperty public static readonly AttachedProperty<Control?> PreviewWithProperty = AvaloniaProperty
.RegisterAttached<AvaloniaObject, Control?>("PreviewWith", typeof (Design)); .RegisterAttached<AvaloniaObject, Control?>("PreviewWith", typeof (Design));
/// <summary>
/// Sets a preview template for the specified <see cref="AvaloniaObject"/> at design-time.
/// </summary>
/// <remarks>
/// This method allows you to specify a substitute control to be rendered in the previewer
/// for a given object.
/// </remarks>
/// <param name="target">The target object.</param>
/// <param name="control">The preview control.</param>
// TODO12: Remove this overload in Avalonia 12
[Obsolete("Use SetPreviewWith(AvaloniaObject, ITemplate<Control>) overload instead. Use <Template></Template> from XAML")]
public static void SetPreviewWith(AvaloniaObject target, Control? control)
{
s_previewWith[target] = control is not null ? new FuncTemplate<Control>(() => control) : null;
}
/// <summary> /// <summary>
/// Sets a preview template for the specified <see cref="AvaloniaObject"/> at design-time. /// Sets a preview template for the specified <see cref="AvaloniaObject"/> at design-time.

10
src/Avalonia.Controls/Documents/Inline.cs

@ -10,11 +10,10 @@ namespace Avalonia.Controls.Documents
/// </summary> /// </summary>
public abstract class Inline : TextElement public abstract class Inline : TextElement
{ {
// TODO12: change the field type to an AttachedProperty for consistency (breaking change)
/// <summary> /// <summary>
/// AvaloniaProperty for <see cref="TextDecorations" /> property. /// AvaloniaProperty for <see cref="TextDecorations" /> property.
/// </summary> /// </summary>
public static readonly StyledProperty<TextDecorationCollection?> TextDecorationsProperty = public static readonly AttachedProperty<TextDecorationCollection?> TextDecorationsProperty =
AvaloniaProperty.RegisterAttached<Inline, Inline, TextDecorationCollection?>( AvaloniaProperty.RegisterAttached<Inline, Inline, TextDecorationCollection?>(
nameof(TextDecorations), nameof(TextDecorations),
inherits: true); inherits: true);
@ -82,12 +81,13 @@ namespace Avalonia.Controls.Documents
return new GenericTextRunProperties( return new GenericTextRunProperties(
typeface, typeface,
FontFeatures,
FontSize, FontSize,
TextDecorations, TextDecorations,
Foreground, Foreground,
parentOrSelfBackground, parentOrSelfBackground,
BaselineAlignment); BaselineAlignment,
null,
FontFeatures);
} }
/// <summary> /// <summary>

37
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -346,13 +346,26 @@ namespace Avalonia.Controls.Presenters
private TextLayout CreateTextLayoutInternal(Size constraint, string? text, Typeface typeface, private TextLayout CreateTextLayoutInternal(Size constraint, string? text, Typeface typeface,
IReadOnlyList<ValueSpan<TextRunProperties>>? textStyleOverrides) IReadOnlyList<ValueSpan<TextRunProperties>>? textStyleOverrides)
{ {
var foreground = Foreground;
var maxWidth = MathUtilities.IsZero(constraint.Width) ? double.PositiveInfinity : constraint.Width; var maxWidth = MathUtilities.IsZero(constraint.Width) ? double.PositiveInfinity : constraint.Width;
var maxHeight = MathUtilities.IsZero(constraint.Height) ? double.PositiveInfinity : constraint.Height; var maxHeight = MathUtilities.IsZero(constraint.Height) ? double.PositiveInfinity : constraint.Height;
var textLayout = new TextLayout(text, typeface, FontFeatures, FontSize, foreground, TextAlignment, var textLayout = new TextLayout(
TextWrapping, maxWidth: maxWidth, maxHeight: maxHeight, textStyleOverrides: textStyleOverrides, text,
flowDirection: FlowDirection, lineHeight: LineHeight, letterSpacing: LetterSpacing); typeface,
FontSize,
Foreground,
TextAlignment,
TextWrapping,
null,
null,
FlowDirection,
maxWidth,
maxHeight,
LineHeight,
LetterSpacing,
0,
FontFeatures,
textStyleOverrides);
return textLayout; return textLayout;
} }
@ -553,9 +566,12 @@ namespace Avalonia.Controls.Presenters
if (!string.IsNullOrEmpty(preeditText)) if (!string.IsNullOrEmpty(preeditText))
{ {
var preeditHighlight = new ValueSpan<TextRunProperties>(caretIndex, preeditText.Length, var preeditHighlight = new ValueSpan<TextRunProperties>(caretIndex, preeditText.Length,
new GenericTextRunProperties(typeface, FontFeatures, FontSize, new GenericTextRunProperties(
foregroundBrush: foreground, typeface,
textDecorations: TextDecorations.Underline)); FontSize,
TextDecorations.Underline,
foreground,
fontFeatures: FontFeatures));
textStyleOverrides = new[] textStyleOverrides = new[]
{ {
@ -569,8 +585,11 @@ namespace Avalonia.Controls.Presenters
textStyleOverrides = new[] textStyleOverrides = new[]
{ {
new ValueSpan<TextRunProperties>(start, length, new ValueSpan<TextRunProperties>(start, length,
new GenericTextRunProperties(typeface, FontFeatures, FontSize, new GenericTextRunProperties(
foregroundBrush: SelectionForegroundBrush)) typeface,
FontSize,
foregroundBrush: SelectionForegroundBrush,
fontFeatures: FontFeatures))
}; };
} }
} }

6
src/Avalonia.Controls/Primitives/TextSearch.cs

@ -24,22 +24,20 @@ namespace Avalonia.Controls.Primitives
public static readonly AttachedProperty<BindingBase?> TextBindingProperty public static readonly AttachedProperty<BindingBase?> TextBindingProperty
= AvaloniaProperty.RegisterAttached<Interactive, BindingBase?>("TextBinding", typeof(TextSearch)); = AvaloniaProperty.RegisterAttached<Interactive, BindingBase?>("TextBinding", typeof(TextSearch));
// TODO12: Control should be Interactive to match the property definition.
/// <summary> /// <summary>
/// Sets the value of the <see cref="TextProperty"/> attached property to a given <see cref="Control"/>. /// Sets the value of the <see cref="TextProperty"/> attached property to a given <see cref="Control"/>.
/// </summary> /// </summary>
/// <param name="control">The control.</param> /// <param name="control">The control.</param>
/// <param name="text">The search text to set.</param> /// <param name="text">The search text to set.</param>
public static void SetText(Control control, string? text) public static void SetText(Interactive control, string? text)
=> control.SetValue(TextProperty, text); => control.SetValue(TextProperty, text);
// TODO12: Control should be Interactive to match the property definition.
/// <summary> /// <summary>
/// Gets the value of the <see cref="TextProperty"/> attached property from a given <see cref="Control"/>. /// Gets the value of the <see cref="TextProperty"/> attached property from a given <see cref="Control"/>.
/// </summary> /// </summary>
/// <param name="control">The control.</param> /// <param name="control">The control.</param>
/// <returns>The search text.</returns> /// <returns>The search text.</returns>
public static string? GetText(Control control) public static string? GetText(Interactive control)
=> control.GetValue(TextProperty); => control.GetValue(TextProperty);
/// <summary> /// <summary>

1
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs

@ -76,6 +76,7 @@ namespace Avalonia.Controls.Remote.Server
Stride, Stride,
new Vector(_dpi, _dpi), new Vector(_dpi, _dpi),
new PlatformPixelFormat((PixelFormatEnum)Format), new PlatformPixelFormat((PixelFormatEnum)Format),
Format == ProtocolPixelFormat.Rgb565 ? AlphaFormat.Opaque : AlphaFormat.Premul,
() => () =>
{ {
handle.Free(); handle.Free();

15
src/Avalonia.Controls/SelectableTextBlock.cs

@ -186,10 +186,10 @@ namespace Avalonia.Controls
var defaultProperties = new GenericTextRunProperties( var defaultProperties = new GenericTextRunProperties(
typeface, typeface,
FontFeatures,
FontSize, FontSize,
TextDecorations, TextDecorations,
Foreground); Foreground,
fontFeatures: FontFeatures);
var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, TextAlignment, true, false, var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, TextAlignment, true, false,
defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing) defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing)
@ -235,9 +235,9 @@ namespace Avalonia.Controls
overlapLength, overlapLength,
new GenericTextRunProperties( new GenericTextRunProperties(
textRun.Properties?.Typeface ?? typeface, textRun.Properties?.Typeface ?? typeface,
textRun.Properties?.FontFeatures ?? FontFeatures,
FontSize, FontSize,
foregroundBrush: SelectionForegroundBrush))); foregroundBrush: SelectionForegroundBrush,
fontFeatures: textRun.Properties?.FontFeatures ?? FontFeatures)));
accumulatedLength += runLength; accumulatedLength += runLength;
} }
@ -247,8 +247,11 @@ namespace Avalonia.Controls
textStyleOverrides = textStyleOverrides =
[ [
new ValueSpan<TextRunProperties>(start, length, new ValueSpan<TextRunProperties>(start, length,
new GenericTextRunProperties(typeface, FontFeatures, FontSize, new GenericTextRunProperties(
foregroundBrush: SelectionForegroundBrush)) typeface,
FontSize,
foregroundBrush: SelectionForegroundBrush,
fontFeatures: FontFeatures))
]; ];
} }
} }

4
src/Avalonia.Controls/TextBlock.cs

@ -657,10 +657,10 @@ namespace Avalonia.Controls
var defaultProperties = new GenericTextRunProperties( var defaultProperties = new GenericTextRunProperties(
typeface, typeface,
FontFeatures,
FontSize, FontSize,
TextDecorations, TextDecorations,
Foreground); Foreground,
fontFeatures: FontFeatures);
var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, IsMeasureValid ? TextAlignment : TextAlignment.Left, true, false, var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, IsMeasureValid ? TextAlignment : TextAlignment.Left, true, false,
defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing) defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing)

1
src/Avalonia.Native/DeferredFramebuffer.cs

@ -29,6 +29,7 @@ namespace Avalonia.Native
public int RowBytes { get; set; } public int RowBytes { get; set; }
public Vector Dpi { get; set; } public Vector Dpi { get; set; }
public PixelFormat Format { get; set; } public PixelFormat Format { get; set; }
public AlphaFormat AlphaFormat { get; set; }
public void Dispose() public void Dispose()
{ {

2
src/Avalonia.X11/X11CursorFactory.cs

@ -133,7 +133,7 @@ namespace Avalonia.X11
return new LockedFramebuffer( return new LockedFramebuffer(
_blob.Address + Marshal.SizeOf<XcursorImage>(), _blob.Address + Marshal.SizeOf<XcursorImage>(),
_pixelSize, _pixelSize.Width * 4, _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); public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock);

2
src/Avalonia.X11/X11FramebufferSurface.cs

@ -61,7 +61,7 @@ namespace Avalonia.X11
{ {
_fb?.Dispose(); _fb?.Dispose();
_fb = null; _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); properties = new FramebufferLockProperties(framebufferValid);

2
src/Avalonia.X11/X11IconLoader.cs

@ -83,7 +83,7 @@ namespace Avalonia.X11
{ {
var h = GCHandle.Alloc(_bdata, GCHandleType.Pinned); var h = GCHandle.Alloc(_bdata, GCHandleType.Pinned);
return new LockedFramebuffer(h.AddrOfPinnedObject(), new PixelSize(_width, _height), _width * 4, 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()); () => h.Free());
} }

4
src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs

@ -47,7 +47,7 @@ partial class BrowserSoftwareRenderTarget : BrowserRenderTarget, IFramebufferPla
{ {
_fb?.Dispose(); _fb?.Dispose();
_fb = null; _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); 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); PutPixelData(Js, fb.Address.ToInt32(), fb.Size.Width * fb.Size.Height * 4, fb.Size.Width, fb.Size.Height);
} }
} }

111
src/Browser/Avalonia.Browser/Rendering/RenderWorker.cs

@ -1,10 +1,7 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.InteropServices.JavaScript; using System.Runtime.InteropServices.JavaScript;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Browser.Interop; using Avalonia.Browser.Interop;
@ -19,11 +16,14 @@ internal partial class RenderWorker
private static partial void InitializeRenderTargets(); private static partial void InitializeRenderTargets();
internal static int WorkerThreadId; 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() public static Task InitializeAsync()
{ {
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var workerTask = JSWebWorkerClone.RunAsync(async () => s_workerTask = JSWebWorkerRunAsync(null, async () =>
{ {
try try
{ {
@ -41,103 +41,18 @@ internal partial class RenderWorker
} }
}); });
workerTask.ContinueWith(_ => s_workerTask.ContinueWith(_ =>
{ {
if (workerTask.IsFaulted) if (s_workerTask.IsFaulted)
tcs.TrySetException(workerTask.Exception); tcs.TrySetException(s_workerTask.Exception);
}); });
return tcs.Task; return tcs.Task;
} }
public static class JSWebWorkerClone // 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 readonly MethodInfo _setExtLoop; private static extern Task JSWebWorkerRunAsync(
private static readonly MethodInfo _intallInterop; [UnsafeAccessorType("System.Runtime.InteropServices.JavaScript.JSWebWorker, System.Runtime.InteropServices.JavaScript")] object? instance,
Func<Task> body);
[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<Task> 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<Task>));
#pragma warning restore IL2075
RunAsync = (Func<Func<Task>, Task>) Delegate.CreateDelegate(typeof(Func<Func<Task>, Task>), m);
}
public static Func<Func<Task>, Task> RunAsync { get; set; }
}
} }

1
src/Headless/Avalonia.Headless/Avalonia.Headless.csproj

@ -6,6 +6,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" /> <ProjectReference Include="..\..\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
<ProjectReference Include="..\..\HarfBuzz\Avalonia.HarfBuzz\Avalonia.HarfBuzz.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\DevAnalyzers.props" /> <Import Project="..\..\..\build\DevAnalyzers.props" />

6
src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -414,8 +414,8 @@ namespace Avalonia.Headless
public Vector Dpi { get; } public Vector Dpi { get; }
public PixelSize PixelSize { get; } public PixelSize PixelSize { get; }
public PixelFormat? Format { get; } public PixelFormat? Format => PixelFormat.Rgba8888;
public AlphaFormat? AlphaFormat { get; } public AlphaFormat? AlphaFormat => Platform.AlphaFormat.Premul;
public int Version { get; set; } public int Version { get; set; }
public void Save(string fileName, int? quality = null) public void Save(string fileName, int? quality = null)
@ -434,7 +434,7 @@ namespace Avalonia.Headless
Version++; Version++;
var mem = Marshal.AllocHGlobal(PixelSize.Width * PixelSize.Height * 4); var mem = Marshal.AllocHGlobal(PixelSize.Width * PixelSize.Height * 4);
return new LockedFramebuffer(mem, PixelSize, PixelSize.Width * 4, Dpi, PixelFormat.Rgba8888, return new LockedFramebuffer(mem, PixelSize, PixelSize.Width * 4, Dpi, PixelFormat.Rgba8888,
() => Marshal.FreeHGlobal(mem)); Platform.AlphaFormat.Premul, () => Marshal.FreeHGlobal(mem));
} }
} }

4
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 windowing subsystem wasn't initialized by user, force headless with default parameters.
if (appBuilder.WindowingSubsystemName != "Headless") if (appBuilder.WindowingSubsystemName != "Headless")
{ {
appBuilder = appBuilder.UseHeadless(new AvaloniaHeadlessPlatformOptions()); appBuilder = appBuilder
.UseHeadless(new AvaloniaHeadlessPlatformOptions())
.UseHarfBuzz();
} }
// ReSharper disable once AccessToModifiedClosure // ReSharper disable once AccessToModifiedClosure

1
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@ -200,6 +200,7 @@ namespace Avalonia.Headless
public int RowBytes => _fb.RowBytes; public int RowBytes => _fb.RowBytes;
public Vector Dpi => _fb.Dpi; public Vector Dpi => _fb.Dpi;
public PixelFormat Format => _fb.Format; public PixelFormat Format => _fb.Format;
public AlphaFormat AlphaFormat => _fb.AlphaFormat;
} }
public ILockedFramebuffer Lock() public ILockedFramebuffer Lock()

11
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, public static LockedFramebuffer LockFb(IntPtr address, fb_var_screeninfo varInfo,
fb_fix_screeninfo fixedInfo, Vector dpi, Action? dispose) 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, return new LockedFramebuffer(address,
new PixelSize((int)varInfo.xres, (int)varInfo.yres), new PixelSize((int)varInfo.xres, (int)varInfo.yres),
(int)fixedInfo.line_length, dpi, (int)fixedInfo.line_length, dpi,
varInfo.bits_per_pixel == 16 ? PixelFormat.Rgb565 format, alphaFormat, dispose);
: varInfo.blue.offset == 16 ? PixelFormat.Rgba8888
: PixelFormat.Bgra8888, dispose);
} }
private void BlitToDevice() private void BlitToDevice()

7
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@ -344,7 +344,8 @@ namespace Avalonia.Markup.Xaml.XamlIl
{ {
EnableIlVerification = true, EnableIlVerification = true,
DefaultCompileBindings = configuration.UseCompiledBindingsByDefault, DefaultCompileBindings = configuration.UseCompiledBindingsByDefault,
IsDesignMode = configuration.DesignMode IsDesignMode = configuration.DesignMode,
CreateSourceInfo = configuration.CreateSourceInfo,
}; };
var parsedDocuments = new List<XamlDocumentResource>(); var parsedDocuments = new List<XamlDocumentResource>();
@ -363,7 +364,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
} }
var parsed = compiler.Parse(xaml, overrideType); var parsed = compiler.Parse(xaml, overrideType);
parsed.Document = "runtimexaml:" + parsedDocuments.Count; parsed.Document = document.Document ?? ("runtimexaml" + parsedDocuments.Count);
compiler.Transform(parsed); compiler.Transform(parsed);
var xamlName = GetSafeUriIdentifier(document.BaseUri) var xamlName = GetSafeUriIdentifier(document.BaseUri)

15
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Avalonia.Markup.Xaml.Loader.CompilerExtensions.Transformers;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX; using XamlX;
@ -20,6 +21,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
private readonly IXamlType _contextType = null!; private readonly IXamlType _contextType = null!;
private readonly AvaloniaXamlIlDesignPropertiesTransformer _designTransformer; private readonly AvaloniaXamlIlDesignPropertiesTransformer _designTransformer;
private readonly AvaloniaBindingExtensionTransformer _bindingTransformer; private readonly AvaloniaBindingExtensionTransformer _bindingTransformer;
private readonly AvaloniaXamlIlAddSourceInfoTransformer _addSourceInfoTransformer;
private readonly AvaloniaXamlResourceTransformer _resourceTransformer;
private AvaloniaXamlIlCompiler(TransformerConfiguration configuration, XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> emitMappings) private AvaloniaXamlIlCompiler(TransformerConfiguration configuration, XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> emitMappings)
: base(configuration, emitMappings, true) : base(configuration, emitMappings, true)
@ -47,6 +50,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlResolveClassesPropertiesTransformer(), new AvaloniaXamlIlResolveClassesPropertiesTransformer(),
new AvaloniaXamlIlTransformInstanceAttachedProperties(), new AvaloniaXamlIlTransformInstanceAttachedProperties(),
new AvaloniaXamlIlTransformSyntheticCompiledBindingMembers()); new AvaloniaXamlIlTransformSyntheticCompiledBindingMembers());
InsertAfter<PropertyReferenceResolver>( InsertAfter<PropertyReferenceResolver>(
new AvaloniaXamlIlAvaloniaPropertyResolver(), new AvaloniaXamlIlAvaloniaPropertyResolver(),
new AvaloniaXamlIlReorderClassesPropertiesTransformer(), new AvaloniaXamlIlReorderClassesPropertiesTransformer(),
@ -85,7 +90,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
); );
InsertBeforeMany(new [] { typeof(DeferredContentTransformer), typeof(AvaloniaXamlIlCompiledBindingsMetadataRemover) }, InsertBeforeMany(new [] { typeof(DeferredContentTransformer), typeof(AvaloniaXamlIlCompiledBindingsMetadataRemover) },
new AvaloniaXamlIlDeferredResourceTransformer()); _resourceTransformer = new AvaloniaXamlResourceTransformer());
InsertBefore<AvaloniaXamlIlTransformInstanceAttachedProperties>(new AvaloniaXamlIlTransformRoutedEvent()); InsertBefore<AvaloniaXamlIlTransformInstanceAttachedProperties>(new AvaloniaXamlIlTransformRoutedEvent());
@ -94,6 +99,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
Transformers.Add(new AvaloniaXamlIlEnsureResourceDictionaryCapacityTransformer()); Transformers.Add(new AvaloniaXamlIlEnsureResourceDictionaryCapacityTransformer());
Transformers.Add(new AvaloniaXamlIlRootObjectScope()); Transformers.Add(new AvaloniaXamlIlRootObjectScope());
Transformers.Add(_addSourceInfoTransformer = new AvaloniaXamlIlAddSourceInfoTransformer());
Emitters.Add(new AvaloniaNameScopeRegistrationXamlIlNodeEmitter()); Emitters.Add(new AvaloniaNameScopeRegistrationXamlIlNodeEmitter());
Emitters.Add(new AvaloniaXamlIlRootObjectScope.Emitter()); Emitters.Add(new AvaloniaXamlIlRootObjectScope.Emitter());
@ -122,6 +129,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public const string PopulateName = "__AvaloniaXamlIlPopulate"; public const string PopulateName = "__AvaloniaXamlIlPopulate";
public const string BuildName = "__AvaloniaXamlIlBuild"; public const string BuildName = "__AvaloniaXamlIlBuild";
public bool CreateSourceInfo
{
get => _addSourceInfoTransformer.CreateSourceInfo || _resourceTransformer.CreateSourceInfo;
set => _addSourceInfoTransformer.CreateSourceInfo = _resourceTransformer.CreateSourceInfo = value;
}
public bool IsDesignMode public bool IsDesignMode
{ {
get => _designTransformer.IsDesignMode; get => _designTransformer.IsDesignMode;

33
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) public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node)
{ {
if (node is not XamlValueWithManipulationNode valueNode // Filter object initialization nodes like:
|| valueNode.Value is not XamlAstNewClrObjectNode objectNode // > XamlValueWithManipulationNode
// > > XamlAstNewClrObjectNode // StyleInclude or ResourceInclude, can be nested in another XamlValueWithManipulationNode
// > > XamlObjectInitializationNode
if (node is not XamlValueWithManipulationNode { Manipulation: XamlObjectInitializationNode initializationNode } valueNode
|| valueNode.UnwrapValue<XamlAstNewClrObjectNode>() is not { } objectNode
|| (objectNode.Type.GetClrType() != context.GetAvaloniaTypes().StyleInclude || (objectNode.Type.GetClrType() != context.GetAvaloniaTypes().StyleInclude
&& objectNode.Type.GetClrType() != context.GetAvaloniaTypes().ResourceInclude)) && 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"); 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<IXamlAstManipulationNode>(); var additionalProperties = new List<IXamlAstManipulationNode>();
if (initializationNode.Manipulation is not XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty) if (initializationNode.Manipulation is not XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty)
{ {
@ -176,9 +175,25 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
strictSourceValueType ? XamlDiagnosticSeverity.Error : XamlDiagnosticSeverity.Warning, strictSourceValueType ? XamlDiagnosticSeverity.Error : XamlDiagnosticSeverity.Warning,
$"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri. This {nodeTypeName} will be resolved in runtime instead.", $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri. This {nodeTypeName} will be resolved in runtime instead.",
node); node);
// We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`. // We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`.
if (sourceProperty.Values.OfType<XamlAstNewClrObjectNode>().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<XamlAstNewClrObjectNode>(),
_ => null
};
// Validate Uri type and constant arguments.
if (sourceUriNode is null
|| sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri || sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri
|| sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath } || sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath }
|| sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind }) || sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind })

69
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
{
/// <summary>
/// An XAMLIL AST transformer that injects <see cref="Avalonia.Markup.Xaml.Diagnostics.XamlSourceInfo"/> metadata into the generated XAML code.
/// </summary>
/// <remarks>
/// 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 <see cref="AvaloniaXamlResourceTransformer"/>.
/// </remarks>
internal class AvaloniaXamlIlAddSourceInfoTransformer : IXamlAstTransformer
{
/// <summary>
/// Gets or sets a value indicating whether source information should be generated
/// and injected into the compiled XAML output.
/// </summary>
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<IXamlILEmitter, XamlILNodeEmitResult> 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);
}
}
}
}

191
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs

@ -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<IXamlPropertySetter>
{
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<IXamlPropertySetter>
{
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<AdderSetter>
{
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<IXamlType> Parameters { get; }
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes => _adder.CustomAttributes;
public void Emit(IXamlILEmitter emitter)
{
var locals = new Stack<XamlLocalsPool.PooledLocal>();
// 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<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> 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();
}
}
}

343
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
{
/// <summary>
/// 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.
/// </summary>
internal class AvaloniaXamlResourceTransformer : IXamlAstTransformer
{
/// <summary>
/// Gets or sets a value indicating whether source information should be generated
/// and injected into the compiled XAML output.
/// </summary>
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<IXamlPropertySetter>
{
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<IXamlPropertySetter>
{
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<AdderSetter>
{
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;
/// <summary>
/// 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);
/// </summary>
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
};
}
/// <summary>
/// Explicit target getter - target will be obtained by calling the getter first.
///
/// </summary>
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<IXamlType> Parameters { get; }
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes => _adder.CustomAttributes;
/// <summary>
/// Emits the setter with arguments already on the stack.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public void Emit(IXamlILEmitter emitter)
{
using var keyLocal = emitter.LocalsPool.GetLocal(Parameters[0]);
if (_getter is not null || _emitSourceInfo)
{
var locals = new Stack<XamlLocalsPool.PooledLocal>();
// 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);
}
}
/// <summary>
/// Emits the setter with provided arguments that are not yet on the stack.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public void EmitWithArguments(
XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> 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();
}
}
}

12
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 ControlTemplate { get; }
public IXamlType EventHandlerT { get; } public IXamlType EventHandlerT { get; }
public IXamlMethod GetClassProperty { get; } public IXamlMethod GetClassProperty { get; }
public IXamlConstructor XamlSourceInfoConstructor { get; }
public IXamlMethod XamlSourceInfoSetter { get; }
public IXamlMethod XamlSourceInfoDictionarySetter { get; }
sealed internal class InteractivityWellKnownTypes sealed internal class InteractivityWellKnownTypes
{ {
@ -343,6 +346,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
allowDowncast:false, allowDowncast:false,
cfg.WellKnownTypes.String 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);
} }
} }

26
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
{
/// <summary>
/// Tries to resolve the underlying value of a <see cref="XamlValueWithManipulationNode"/>,
/// unwrapping any nested <see cref="XamlValueWithManipulationNode"/> instances.
/// </summary>
public static TXamlAstValueNode? UnwrapValue<TXamlAstValueNode>(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;
}
}

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -38,6 +38,7 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RuntimeXamlLoaderConfiguration.cs" /> <Compile Include="RuntimeXamlLoaderConfiguration.cs" />
<Compile Include="RuntimeXamlLoaderDocument.cs" /> <Compile Include="RuntimeXamlLoaderDocument.cs" />
<Compile Include="Diagnostics\XamlSourceInfo.cs" />
<Compile Include="Styling\MergeResourceInclude.cs" /> <Compile Include="Styling\MergeResourceInclude.cs" />
<Compile Include="Styling\ResourceInclude.cs" /> <Compile Include="Styling\ResourceInclude.cs" />
<Compile Include="Styling\StyleInclude.cs" /> <Compile Include="Styling\StyleInclude.cs" />

151
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
{
/// <summary>
/// Represents source location information for an element within a XAML or code file.
/// </summary>
// ReSharper disable once ClassNeverInstantiated.Global //This class is instantiated through the XAML compiler.
public record XamlSourceInfo
{
private static readonly AttachedProperty<XamlSourceInfo?> s_xamlSourceInfo =
AvaloniaProperty.RegisterAttached<AvaloniaObject, XamlSourceInfo?>(
"XamlSourceInfo", typeof(XamlSourceInfo));
private static readonly ConditionalWeakTable<object, XamlSourceInfo?> s_sourceInfo = [];
private static readonly ConditionalWeakTable<IResourceDictionary, Dictionary<object, XamlSourceInfo?>> s_keyedSourceInfo = [];
/// <summary>
/// Gets the full path of the source file containing the element, or <c>null</c> if unavailable.
/// </summary>
public Uri? SourceUri { get; }
/// <summary>
/// Gets the 1-based line number in the source file where the element is defined.
/// </summary>
public int LineNumber { get; }
/// <summary>
/// Gets the 1-based column number in the source file where the element is defined.
/// </summary>
public int LinePosition { get; }
/// <summary>
/// Initializes a new instance of the <see cref="XamlSourceInfo"/> class
/// with a specified line, column, and file path.
/// </summary>
/// <param name="line">The line number of the source element.</param>
/// <param name="column">The column number of the source element.</param>
/// <param name="filePath">The full path of the source file.</param>
public XamlSourceInfo(int line, int column, string? filePath)
{
LineNumber = line;
LinePosition = column;
SourceUri = filePath is not null ? new UriBuilder("file", "") { Path = filePath }.Uri : null;
}
/// <summary>
/// Associates XAML source information with the specified object for debugging or diagnostic purposes.
/// </summary>
/// <remarks>This method is typically used to enable enhanced debugging or diagnostics by tracking
/// the origin of XAML elements at runtime. If the same object is passed multiple times, the most recent source
/// information will overwrite any previous value.</remarks>
/// <param name="obj">The object to associate with the XAML source information. Cannot be null.</param>
/// <param name="info">The XAML source information to associate with the object, or null to remove any existing association.</param>
public static void SetXamlSourceInfo(object obj, XamlSourceInfo? info)
{
if (obj is null)
throw new ArgumentNullException(nameof(obj));
if (obj is AvaloniaObject avaloniaObject)
{
avaloniaObject.SetValue(s_xamlSourceInfo, info);
}
else
{
s_sourceInfo.AddOrUpdate(obj, info);
}
}
/// <summary>
/// Associates XAML source information with the specified key in the given resource dictionary.
/// </summary>
/// <param name="dictionary"> The resource dictionary to associate with the XAML source information.</param>
/// <param name="key">The key associated with the source info.</param>
/// <param name="info">The XAML source information to associate with the object, or null to remove any existing association.</param>
public static void SetXamlSourceInfo(IResourceDictionary dictionary, object key, XamlSourceInfo? info)
{
if (dictionary is null)
throw new ArgumentNullException(nameof(dictionary));
var dict = s_keyedSourceInfo.GetOrCreateValue(dictionary);
if (info == null)
{
_ = dict.Remove(key);
}
else
{
dict[key] = info;
}
}
/// <summary>
/// Retrieves the XAML source information associated with the specified object, if available.
/// </summary>
/// <param name="obj">The object for which to obtain XAML source information. Cannot be null.</param>
/// <returns>A <see cref="XamlSourceInfo"/> instance containing the XAML source information for the specified object, or
/// <see langword="null"/> if no source information is available.</returns>
public static XamlSourceInfo? GetXamlSourceInfo(object obj)
{
if (obj is null)
throw new ArgumentNullException(nameof(obj));
if (obj is AvaloniaObject avaloniaObject)
{
return avaloniaObject.GetValue(s_xamlSourceInfo);
}
else
{
s_sourceInfo.TryGetValue(obj, out var info);
return info;
}
}
/// <summary>
/// Retrieves the XAML source information associated with the specified key in the given resource dictionary, if available.
/// </summary>
/// <param name="dictionary"> The resource dictionary associated with the XAML source information.</param>
/// <param name="key">The key associated with the source info.</param>
/// <returns>A <see cref="XamlSourceInfo"/> instance containing the XAML source information for the specified key, or
/// <see langword="null"/> if no source information is available.</returns>
public static XamlSourceInfo? GetXamlSourceInfo(IResourceDictionary dictionary, object key)
{
if (dictionary is null)
throw new ArgumentNullException(nameof(dictionary));
if (s_keyedSourceInfo.TryGetValue(dictionary, out var dict)
&& dict.TryGetValue(key, out var info))
{
return info;
}
return null;
}
/// <summary>
/// Returns a string that represents the current <see cref="XamlSourceInfo"/>.
/// </summary>
/// <returns>
/// A formatted string in the form <c>"FilePath:Line,Column"</c>,
/// or <c>"(unknown):Line,Column"</c> if the file path is not set.
/// </returns>
public override string ToString()
{
var filePath = SourceUri?.LocalPath ?? "(unknown)";
return $"{filePath}:{LineNumber},{LinePosition}";
}
}
}

6
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs

@ -22,6 +22,12 @@ public class RuntimeXamlLoaderConfiguration
/// </summary> /// </summary>
public bool DesignMode { get; set; } = false; public bool DesignMode { get; set; } = false;
/// <summary>
/// When enabled, the XAML compiler embeds SourceInfo metadata (file path, line, and column) into generated code.
/// Default is 'false'.
/// </summary>
public bool CreateSourceInfo { get; set; } = false;
/// <summary> /// <summary>
/// XAML diagnostics handler. /// XAML diagnostics handler.
/// </summary> /// </summary>

5
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs

@ -58,6 +58,11 @@ public class RuntimeXamlLoaderDocument
/// </summary> /// </summary>
public Uri? BaseUri { get; set; } public Uri? BaseUri { get; set; }
/// <summary>
/// Path to the XAML document being loaded.
/// </summary>
public string? Document { get; set; }
/// <summary> /// <summary>
/// The optional instance into which the XAML should be loaded. /// The optional instance into which the XAML should be loaded.
/// </summary> /// </summary>

2
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -67,7 +67,7 @@ namespace Avalonia.Skia
var framebuffer = _renderTargetWithProperties?.Lock(out lockProperties) ?? _renderTarget.Lock(); var framebuffer = _renderTargetWithProperties?.Lock(out lockProperties) ?? _renderTarget.Lock();
var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height, var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
framebuffer.Format.ToSkColorType(), framebuffer.Format.ToSkColorType(),
framebuffer.Format == PixelFormat.Rgb565 ? SKAlphaType.Opaque : SKAlphaType.Premul); framebuffer.AlphaFormat.ToSkAlphaType());
CreateSurface(framebufferImageInfo, framebuffer); CreateSurface(framebufferImageInfo, framebuffer);
_hadConversionShim |= _conversionShim != null; _hadConversionShim |= _conversionShim != null;

6
src/Skia/Avalonia.Skia/ImmutableBitmap.cs

@ -10,7 +10,7 @@ namespace Avalonia.Skia
/// <summary> /// <summary>
/// Immutable Skia bitmap. /// Immutable Skia bitmap.
/// </summary> /// </summary>
internal class ImmutableBitmap : IDrawableBitmapImpl, IReadableBitmapWithAlphaImpl internal class ImmutableBitmap : IDrawableBitmapImpl, IReadableBitmapImpl
{ {
private readonly SKImage _image; private readonly SKImage _image;
private readonly SKBitmap? _bitmap; private readonly SKBitmap? _bitmap;
@ -195,7 +195,9 @@ namespace Avalonia.Skia
if (_bitmap.ColorType.ToAvalonia() is not { } format) if (_bitmap.ColorType.ToAvalonia() is not { } format)
throw new NotSupportedException($"Unsupported format {_bitmap.ColorType}"); 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);
} }
} }
} }

2
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -238,6 +238,8 @@ namespace Avalonia.Skia
public Vector Dpi => _parent.Dpi; public Vector Dpi => _parent.Dpi;
/// <inheritdoc /> /// <inheritdoc />
public PixelFormat Format => _bitmap.ColorType.ToPixelFormat(); public PixelFormat Format => _bitmap.ColorType.ToPixelFormat();
public AlphaFormat AlphaFormat => _bitmap.AlphaType.ToAlphaFormat();
} }
} }
} }

2
src/Windows/Avalonia.Win32/FramebufferManager.cs

@ -49,7 +49,7 @@ namespace Avalonia.Win32
return fb = new LockedFramebuffer( return fb = new LockedFramebuffer(
framebufferData.Data.Address, framebufferData.Size, framebufferData.RowBytes, framebufferData.Data.Address, framebufferData.Size, framebufferData.RowBytes,
GetCurrentDpi(), s_format, _onDisposeAction); GetCurrentDpi(), s_format, AlphaFormat.Premul, _onDisposeAction);
} }
finally finally
{ {

14
src/Windows/Avalonia.Win32/Interop/Win32Icon.cs

@ -96,14 +96,14 @@ internal class Win32Icon : IDisposable
static IntPtr CreateHBitmap(Bitmap source) static IntPtr CreateHBitmap(Bitmap source)
{ {
using var fb = AllocFramebuffer(source.PixelSize, PixelFormats.Bgra8888); using var fb = AllocFramebuffer(source.PixelSize, PixelFormats.Bgra8888, AlphaFormat.Unpremul);
source.CopyPixels(fb, AlphaFormat.Unpremul); source.CopyPixels(fb);
return UnmanagedMethods.CreateBitmap(source.PixelSize.Width, source.PixelSize.Height, 1, 32, fb.Address); return UnmanagedMethods.CreateBitmap(source.PixelSize.Width, source.PixelSize.Height, 1, 32, fb.Address);
} }
static unsafe IntPtr AlphaToMask(Bitmap source) 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 height = alphaMaskBuffer.Size.Height;
var width = alphaMaskBuffer.Size.Width; var width = alphaMaskBuffer.Size.Width;
@ -114,8 +114,8 @@ internal class Win32Icon : IDisposable
} }
else else
{ {
using var argbBuffer = AllocFramebuffer(source.PixelSize, PixelFormat.Bgra8888); using var argbBuffer = AllocFramebuffer(source.PixelSize, PixelFormat.Bgra8888, AlphaFormat.Unpremul);
source.CopyPixels(argbBuffer, AlphaFormat.Unpremul); source.CopyPixels(argbBuffer);
var pSource = (byte*)argbBuffer.Address; var pSource = (byte*)argbBuffer.Address;
var pDest = (byte*)alphaMaskBuffer.Address; var pDest = (byte*)alphaMaskBuffer.Address;
@ -140,7 +140,7 @@ internal class Win32Icon : IDisposable
return UnmanagedMethods.CreateBitmap(width, height, 1, 1, alphaMaskBuffer.Address); 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) if (size.Width < 1 || size.Height < 1)
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
@ -149,7 +149,7 @@ internal class Win32Icon : IDisposable
var data = Marshal.AllocHGlobal(size.Height * stride); var data = Marshal.AllocHGlobal(size.Height * stride);
if (data == IntPtr.Zero) if (data == IntPtr.Zero)
throw new OutOfMemoryException(); 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)); () => Marshal.FreeHGlobal(data));
} }

81
tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs

@ -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<FormatException>(() => 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<FormatException>(() => 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()));
}
}
}

10
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs

@ -12,8 +12,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{ {
public class ResourceIncludeTests : XamlTestBase public class ResourceIncludeTests : XamlTestBase
{ {
[Fact] [Theory]
public void ResourceInclude_Loads_ResourceDictionary() [InlineData(false)]
[InlineData(true)]
public void ResourceInclude_Loads_ResourceDictionary(bool createSourceInfo)
{ {
var documents = new[] var documents = new[]
{ {
@ -37,9 +39,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
</UserControl>") </UserControl>")
}; };
var config = new RuntimeXamlLoaderConfiguration { CreateSourceInfo = createSourceInfo };
using (StartWithResources()) using (StartWithResources())
{ {
var compiled = AvaloniaRuntimeXamlLoader.LoadGroup(documents); var compiled = AvaloniaRuntimeXamlLoader.LoadGroup(documents, config);
var userControl = Assert.IsType<UserControl>(compiled[1]); var userControl = Assert.IsType<UserControl>(compiled[1]);
var border = userControl.GetControl<Border>("border"); var border = userControl.GetControl<Border>("border");

11
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs

@ -18,8 +18,10 @@ public class MergeResourceIncludeTests : XamlTestBase
RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle); RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle);
} }
[Fact] [Theory]
public void MergeResourceInclude_Works_With_Single_Resource() [InlineData(false)]
[InlineData(true)]
public void MergeResourceInclude_Works_With_Single_Resource(bool createSourceInfo)
{ {
var documents = new[] var documents = new[]
{ {
@ -41,8 +43,9 @@ public class MergeResourceIncludeTests : XamlTestBase
</UserControl.Resources> </UserControl.Resources>
</UserControl>") </UserControl>")
}; };
var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents); var config = new RuntimeXamlLoaderConfiguration { CreateSourceInfo = createSourceInfo };
var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents, config);
var contentControl = Assert.IsType<UserControl>(objects[1]); var contentControl = Assert.IsType<UserControl>(objects[1]);
var resources = Assert.IsType<ResourceDictionary>(contentControl.Resources); var resources = Assert.IsType<ResourceDictionary>(contentControl.Resources);

4
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs

@ -265,6 +265,8 @@ public class StyleIncludeTests : XamlTestBase
[Fact] [Fact]
public void StyleInclude_Should_Be_Replaced_With_Direct_Call() 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(@"
<ContentControl xmlns='https://github.com/avaloniaui' <ContentControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
@ -281,6 +283,8 @@ public class StyleIncludeTests : XamlTestBase
[Fact] [Fact]
public void Style_Inside_Resources_Should_Produce_Warning() public void Style_Inside_Resources_Should_Produce_Warning()
{ {
using var _ = UnitTestApplication.Start(TestServices.StyledWindow);
var diagnostics = new List<RuntimeXamlDiagnostic>(); var diagnostics = new List<RuntimeXamlDiagnostic>();
var control = (ContentControl)AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(@" var control = (ContentControl)AvaloniaRuntimeXamlLoader.Load(new RuntimeXamlLoaderDocument(@"
<ContentControl xmlns='https://github.com/avaloniaui' <ContentControl xmlns='https://github.com/avaloniaui'

583
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlSourceInfoTests.cs

@ -0,0 +1,583 @@
using System;
using System.Linq;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.GestureRecognizers;
using Avalonia.Markup.Xaml.Diagnostics;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media;
using Avalonia.Styling;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
public class XamlSourceInfoTests : XamlTestBase
{
private static readonly RuntimeXamlLoaderConfiguration s_configuration = new RuntimeXamlLoaderConfiguration
{
CreateSourceInfo = true
};
[Theory]
[InlineData(@"C:\TestFolder\TestFile.xaml")] // Windows-style path
[InlineData("/TestFolder/TestFile.xaml")] // Unix-style path
public void Root_UserControl_With_BaseUri_Gets_XamlSourceInfo_SourceUri_Set(string document)
{
var xamlDocument = new RuntimeXamlLoaderDocument(
"""
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
</UserControl>
""")
{
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<StackPanel>
<Button />
<TextBlock />
</StackPanel>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Rectangle Fill=""Blue"" Width=""63"" Height=""41"">
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint=""0%,0%"" EndPoint=""100%,100%"">
<LinearGradientBrush.GradientStops>
<GradientStop Offset=""0"" Color=""Black""/>
<GradientStop Offset=""1"" Color=""Transparent""/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.OpacityMask>
</Rectangle>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Canvas Name=""TheCanvas"" Background=""Yellow"" Width=""300"" Height=""400"">
<Ellipse Fill=""Green"" Width=""58"" Height=""58"" Canvas.Left=""88"" Canvas.Top=""100""/>
<Path Fill=""Orange"" Canvas.Left=""30"" Canvas.Top=""250""/>
<Path Fill=""OrangeRed"" Canvas.Left=""180"" Canvas.Top=""250"">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint=""0,0"" IsClosed=""True"">
<QuadraticBezierSegment Point1=""50,0"" Point2=""50,-50"" />
<QuadraticBezierSegment Point1=""100,-50"" Point2=""100,0"" />
<LineSegment Point=""50,0"" />
<LineSegment Point=""50,50"" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Line StartPoint=""120,185"" EndPoint=""30,115"" Stroke=""Red"" StrokeThickness=""2""/>
<Polygon Points=""75,0 120,120 0,45 150,45 30,120"" Stroke=""DarkBlue"" StrokeThickness=""1"" Fill=""Violet"" Canvas.Left=""150"" Canvas.Top=""31""/>
</Canvas>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Styles>
<Style Selector=""Button"">
<Setter Property=""Margin"" Value=""5"" />
</Style>
<ContainerQuery Name=""container""
Query=""max-width:400"">
<Style Selector=""Button"">
<Setter Property=""Background""
Value=""Red""/>
</Style>
</ContainerQuery>
</UserControl.Styles>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Styles>
<Style Selector=""Rectangle.red"">
<Setter Property=""Fill"" Value=""Red""/>
<Style.Animations>
<Animation Duration=""0:0:3"">
<KeyFrame Cue=""0%"">
<Setter Property=""Opacity"" Value=""0.0""/>
</KeyFrame>
<KeyFrame Cue=""100%"">
<Setter Property=""Opacity"" Value=""1.0""/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</UserControl.Styles>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='using:Avalonia.Markup.Xaml.UnitTests.Xaml'>
<UserControl.DataTemplates>
<DataTemplate DataType=""local:SourceInfoTestViewModel"">
<Border Background=""Red"" CornerRadius=""8"">
<TextBox Text=""{Binding Name}""/>
</Border>
</DataTemplate>
</UserControl.DataTemplates>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key='Light'>
<SolidColorBrush x:Key='BackgroundBrush' Color='White'/>
<SolidColorBrush x:Key='ForegroundBrush' Color='Black'/>
</ResourceDictionary>
<ResourceDictionary x:Key='Dark'>
<SolidColorBrush x:Key='BackgroundBrush' Color='Black'/>
<SolidColorBrush x:Key='ForegroundBrush' Color='White'/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<SolidColorBrush x:Key=""Background"" Color=""Yellow"" />
<SolidColorBrush x:Key='OtherBrush'>Black</SolidColorBrush>
</ResourceDictionary>
</UserControl.Resources>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Resources>
<x:String x:Key='text'>foobar</x:String>
<x:Double x:Key=""A_Double"">123.3</x:Double>
<x:Int16 x:Key=""An_Int16"">123</x:Int16>
<x:Int32 x:Key=""An_Int32"">37434323</x:Int32>
<Thickness x:Key=""PreferredPadding"">10,20,10,0</Thickness>
</UserControl.Resources>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Resources>
<x:String x:Key='text'>foobar</x:String>
<x:Double x:Key=""A_Double"">123.3</x:Double>
<x:Int16 x:Key=""An_Int16"">123</x:Int16>
<x:Int32 x:Key=""An_Int32"">37434323</x:Int32>
<Thickness x:Key=""PreferredPadding"">10,20,10,0</Thickness>
<x:Uri x:Key='homepage'>http://avaloniaui.net</x:Uri>
<SolidColorBrush x:Key='MyBrush' Color='Red'/>
</UserControl.Resources>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Resources>
<ResourceDictionary>
<x:String x:Key='text'>foobar</x:String>
<x:Double x:Key=""A_Double"">123.3</x:Double>
<x:Int16 x:Key=""An_Int16"">123</x:Int16>
<x:Int32 x:Key=""An_Int32"">37434323</x:Int32>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Thickness x:Key=""PreferredPadding"">10,20,10,0</Thickness>
<x:Uri x:Key='homepage'>http://avaloniaui.net</x:Uri>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key='Light'>
<SolidColorBrush x:Key='MyBrush' Color='Red'/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</UserControl.Resources>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.GestureRecognizers>
<ScrollGestureRecognizer CanHorizontallyScroll=""True""
CanVerticallyScroll=""True""/>
<PullGestureRecognizer PullDirection=""TopToBottom""/>
</UserControl.GestureRecognizers>
</UserControl>");
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(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Transitions>
<Transitions>
<DoubleTransition Property=""Width"" Duration=""0:0:1.5""/>
<DoubleTransition Property=""Height"" Duration=""0:0:1.5""/>
</Transitions>
</UserControl.Transitions>
</UserControl>");
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; }
}
}

2
tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs

@ -83,7 +83,7 @@ public class DirectFbCompositionTests : TestBase
SKBitmap fb = new SKBitmap(200, 200, SKColorType.Rgba8888, SKAlphaType.Premul); SKBitmap fb = new SKBitmap(200, 200, SKColorType.Rgba8888, SKAlphaType.Premul);
ILockedFramebuffer LockFb() => new LockedFramebuffer(fb.GetAddress(0, 0), new(fb.Width, fb.Height), 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; bool previousFrameIsRetained = false;
IFramebufferRenderTarget rt = advertised IFramebufferRenderTarget rt = advertised

7
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -25,10 +25,11 @@ namespace Avalonia.Skia.RenderTests
class Framebuffer : ILockedFramebuffer, IFramebufferPlatformSurface class Framebuffer : ILockedFramebuffer, IFramebufferPlatformSurface
{ {
public Framebuffer(PixelFormat fmt, PixelSize size) public Framebuffer(PixelFormat fmt, AlphaFormat alphaFormat, PixelSize size)
{ {
Format = fmt; Format = fmt;
var bpp = fmt == PixelFormat.Rgb565 ? 2 : 4; var bpp = fmt == PixelFormat.Rgb565 ? 2 : 4;
AlphaFormat = alphaFormat;
Size = size; Size = size;
RowBytes = bpp * size.Width; RowBytes = bpp * size.Width;
Address = Marshal.AllocHGlobal(size.Height * RowBytes); Address = Marshal.AllocHGlobal(size.Height * RowBytes);
@ -40,6 +41,8 @@ namespace Avalonia.Skia.RenderTests
public PixelFormat Format { get; } public PixelFormat Format { get; }
public AlphaFormat AlphaFormat { get; }
public PixelSize Size { get; } public PixelSize Size { get; }
public int RowBytes { get; } public int RowBytes { get; }
@ -64,7 +67,7 @@ namespace Avalonia.Skia.RenderTests
{ {
var fmt = new PixelFormat(fmte); var fmt = new PixelFormat(fmte);
var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt; 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<IPlatformRenderInterface>(); var r = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
using(var cpuContext = r.CreateBackendContext(null)) using(var cpuContext = r.CreateBackendContext(null))
using (var target = cpuContext.CreateRenderTarget(new object[] { fb })) using (var target = cpuContext.CreateRenderTarget(new object[] { fb }))

2
tests/Avalonia.UnitTests/CompositorTestServices.cs

@ -174,7 +174,7 @@ public class CompositorTestServices : IDisposable
{ {
var ptr = Marshal.AllocHGlobal(128); var ptr = Marshal.AllocHGlobal(128);
return new LockedFramebuffer(ptr, new PixelSize(1, 1), 4, new Vector(96, 96), 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); public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock);

Loading…
Cancel
Save