From e43a7be2ddb85946435539713e8a8a2f3132e5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 19 Mar 2026 23:13:41 +0100 Subject: [PATCH 01/14] Bump Avalonia 12 package versions --- Directory.Build.props | 6 +++--- Directory.Packages.props | 19 +++++++++++-------- build/Avalonia.Diagnostics.props | 5 ++++- build/SkiaSharp.Native.v3.props | 8 ++++---- build/SkiaSharp.v3.props | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8c89d9cad2..90a53cf2a6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@  - 3.6.0 - - 11.3.9.4 + 4.0.0 + rc1 + 12.0.0 $(VersionSuffix) Wiesław Šoltés Wiesław Šoltés diff --git a/Directory.Packages.props b/Directory.Packages.props index bd28e25b2e..2f64be399f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,13 +1,13 @@  true - 11.3.9 + 12.0.0-rc1 - + @@ -17,14 +17,16 @@ + + - - - - - - + + + + + + @@ -34,6 +36,7 @@ + diff --git a/build/Avalonia.Diagnostics.props b/build/Avalonia.Diagnostics.props index d809e48ed7..27fb73ea45 100644 --- a/build/Avalonia.Diagnostics.props +++ b/build/Avalonia.Diagnostics.props @@ -1,6 +1,9 @@  - + + false + + diff --git a/build/SkiaSharp.Native.v3.props b/build/SkiaSharp.Native.v3.props index aa0caab38b..96cbc5725f 100644 --- a/build/SkiaSharp.Native.v3.props +++ b/build/SkiaSharp.Native.v3.props @@ -1,9 +1,9 @@ - - - - + + + + diff --git a/build/SkiaSharp.v3.props b/build/SkiaSharp.v3.props index 78c953fb67..cb7734c379 100644 --- a/build/SkiaSharp.v3.props +++ b/build/SkiaSharp.v3.props @@ -1,6 +1,6 @@ - + From f87dd8ce7057b9711b049719c005c6cb02754baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 19 Mar 2026 23:13:46 +0100 Subject: [PATCH 02/14] Retarget Avalonia projects to net8 and net10 --- .../AvaloniaSKPictureImageSample.csproj | 1 + src/Skia.Controls.Avalonia/Skia.Controls.Avalonia.csproj | 2 +- src/Svg.Controls.Avalonia/Svg.Controls.Avalonia.csproj | 2 +- .../Svg.Controls.Skia.Avalonia.csproj | 2 +- src/Svg.Editor.Avalonia/Svg.Editor.Avalonia.csproj | 3 ++- src/Svg.Editor.Skia.Avalonia/Svg.Editor.Skia.Avalonia.csproj | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/samples/AvaloniaSKPictureImageSample/AvaloniaSKPictureImageSample.csproj b/samples/AvaloniaSKPictureImageSample/AvaloniaSKPictureImageSample.csproj index b4b2f1dd23..128bee025c 100644 --- a/samples/AvaloniaSKPictureImageSample/AvaloniaSKPictureImageSample.csproj +++ b/samples/AvaloniaSKPictureImageSample/AvaloniaSKPictureImageSample.csproj @@ -7,6 +7,7 @@ enable latest False + false diff --git a/src/Skia.Controls.Avalonia/Skia.Controls.Avalonia.csproj b/src/Skia.Controls.Avalonia/Skia.Controls.Avalonia.csproj index 703d284c07..72e8a7e667 100644 --- a/src/Skia.Controls.Avalonia/Skia.Controls.Avalonia.csproj +++ b/src/Skia.Controls.Avalonia/Skia.Controls.Avalonia.csproj @@ -2,7 +2,7 @@ Library - netstandard2.0;net461;net6.0;net8.0;net10.0 + net8.0;net10.0 False False CS1591 diff --git a/src/Svg.Controls.Avalonia/Svg.Controls.Avalonia.csproj b/src/Svg.Controls.Avalonia/Svg.Controls.Avalonia.csproj index 8b88412ab8..2ae4cce121 100644 --- a/src/Svg.Controls.Avalonia/Svg.Controls.Avalonia.csproj +++ b/src/Svg.Controls.Avalonia/Svg.Controls.Avalonia.csproj @@ -2,7 +2,7 @@ Library - netstandard2.0;net461;net6.0;net8.0;net10.0 + net8.0;net10.0 False False CS1591 diff --git a/src/Svg.Controls.Skia.Avalonia/Svg.Controls.Skia.Avalonia.csproj b/src/Svg.Controls.Skia.Avalonia/Svg.Controls.Skia.Avalonia.csproj index 64bc2a8830..330ff13f5b 100644 --- a/src/Svg.Controls.Skia.Avalonia/Svg.Controls.Skia.Avalonia.csproj +++ b/src/Svg.Controls.Skia.Avalonia/Svg.Controls.Skia.Avalonia.csproj @@ -2,7 +2,7 @@ Library - netstandard2.0;net461;net6.0;net8.0;net10.0 + net8.0;net10.0 False False CS1591 diff --git a/src/Svg.Editor.Avalonia/Svg.Editor.Avalonia.csproj b/src/Svg.Editor.Avalonia/Svg.Editor.Avalonia.csproj index 3c2bb4d747..da1c45d59b 100644 --- a/src/Svg.Editor.Avalonia/Svg.Editor.Avalonia.csproj +++ b/src/Svg.Editor.Avalonia/Svg.Editor.Avalonia.csproj @@ -2,7 +2,8 @@ Library - netstandard2.0;net461;net6.0;net8.0;net10.0 + net8.0;net10.0 + false False False True diff --git a/src/Svg.Editor.Skia.Avalonia/Svg.Editor.Skia.Avalonia.csproj b/src/Svg.Editor.Skia.Avalonia/Svg.Editor.Skia.Avalonia.csproj index ef93904356..b97f7abd1f 100644 --- a/src/Svg.Editor.Skia.Avalonia/Svg.Editor.Skia.Avalonia.csproj +++ b/src/Svg.Editor.Skia.Avalonia/Svg.Editor.Skia.Avalonia.csproj @@ -2,7 +2,7 @@ Library - netstandard2.0;net461;net6.0;net8.0;net10.0 + net8.0;net10.0 False False True From 293c7cdfc873a43637cb282376b215d4d5755ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 19 Mar 2026 23:13:51 +0100 Subject: [PATCH 03/14] Update Avalonia test projects for xUnit v3 --- build/XUnit.v3.props | 8 ++++++++ .../Avalonia.Svg.Skia.UiTests.csproj | 3 --- .../Svg.Controls.Avalonia.UnitTests.csproj | 4 ++-- .../Svg.Controls.Skia.Avalonia.UnitTests.csproj | 4 ++-- .../Svg.Editor.Skia.Avalonia.UnitTests.csproj | 4 ++-- 5 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 build/XUnit.v3.props diff --git a/build/XUnit.v3.props b/build/XUnit.v3.props new file mode 100644 index 0000000000..8e0946d9c8 --- /dev/null +++ b/build/XUnit.v3.props @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Avalonia.Svg.Skia.UiTests/Avalonia.Svg.Skia.UiTests.csproj b/tests/Avalonia.Svg.Skia.UiTests/Avalonia.Svg.Skia.UiTests.csproj index 138e87ed8e..d2fc89fd08 100644 --- a/tests/Avalonia.Svg.Skia.UiTests/Avalonia.Svg.Skia.UiTests.csproj +++ b/tests/Avalonia.Svg.Skia.UiTests/Avalonia.Svg.Skia.UiTests.csproj @@ -7,13 +7,10 @@ enable - - - diff --git a/tests/Svg.Controls.Avalonia.UnitTests/Svg.Controls.Avalonia.UnitTests.csproj b/tests/Svg.Controls.Avalonia.UnitTests/Svg.Controls.Avalonia.UnitTests.csproj index e9cb1962a7..aad3e975c4 100644 --- a/tests/Svg.Controls.Avalonia.UnitTests/Svg.Controls.Avalonia.UnitTests.csproj +++ b/tests/Svg.Controls.Avalonia.UnitTests/Svg.Controls.Avalonia.UnitTests.csproj @@ -2,14 +2,14 @@ net10.0 - Library + Exe False enable Avalonia.Svg.UnitTests - + diff --git a/tests/Svg.Controls.Skia.Avalonia.UnitTests/Svg.Controls.Skia.Avalonia.UnitTests.csproj b/tests/Svg.Controls.Skia.Avalonia.UnitTests/Svg.Controls.Skia.Avalonia.UnitTests.csproj index 2cab770448..e57e68c140 100644 --- a/tests/Svg.Controls.Skia.Avalonia.UnitTests/Svg.Controls.Skia.Avalonia.UnitTests.csproj +++ b/tests/Svg.Controls.Skia.Avalonia.UnitTests/Svg.Controls.Skia.Avalonia.UnitTests.csproj @@ -2,14 +2,14 @@ net10.0 - Library + Exe False enable Avalonia.Svg.Skia.UnitTests - + diff --git a/tests/Svg.Editor.Skia.Avalonia.UnitTests/Svg.Editor.Skia.Avalonia.UnitTests.csproj b/tests/Svg.Editor.Skia.Avalonia.UnitTests/Svg.Editor.Skia.Avalonia.UnitTests.csproj index 69ed3da87b..eea82096ae 100644 --- a/tests/Svg.Editor.Skia.Avalonia.UnitTests/Svg.Editor.Skia.Avalonia.UnitTests.csproj +++ b/tests/Svg.Editor.Skia.Avalonia.UnitTests/Svg.Editor.Skia.Avalonia.UnitTests.csproj @@ -2,14 +2,14 @@ net10.0 - Library + Exe False enable Svg.Editor.Skia.Avalonia.UnitTests - + From 60022be14aa0f349511023212c5f9704fa67f71b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 19 Mar 2026 23:14:00 +0100 Subject: [PATCH 04/14] Migrate Avalonia 12 APIs in sources and samples --- .../MainWindow.axaml.cs | 3 - .../MainWindow.axaml.cs | 3 - samples/AvaloniaSvgSample/MainWindow.axaml.cs | 66 +++++----- .../AvaloniaSvgSkiaSample/MainWindow.axaml.cs | 78 ++++++------ .../MainWindow.axaml.cs | 3 - .../AvaloniaSvgAssetLoader.cs | 13 +- .../InsertElementPickerView.axaml.cs | 2 +- .../SymbolPickerView.axaml.cs | 2 +- .../SvgEditorWorkspace.axaml.cs | 120 +++++++++--------- src/Svg.Skia/SkiaModel.cs | 2 +- 10 files changed, 143 insertions(+), 149 deletions(-) diff --git a/samples/AvaloniaControlsSample/MainWindow.axaml.cs b/samples/AvaloniaControlsSample/MainWindow.axaml.cs index 1ae19c5b1e..f4946e555d 100644 --- a/samples/AvaloniaControlsSample/MainWindow.axaml.cs +++ b/samples/AvaloniaControlsSample/MainWindow.axaml.cs @@ -9,9 +9,6 @@ public partial class MainWindow : Window public MainWindow() { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif CanvasControl.Draw += (_, e) => { e.Canvas.DrawRect(SKRect.Create(0f, 0f, 100f, 100f), new SKPaint { Color = SKColors.Aqua }); diff --git a/samples/AvaloniaSKPictureImageSample/MainWindow.axaml.cs b/samples/AvaloniaSKPictureImageSample/MainWindow.axaml.cs index c314fbea7e..5dd82b5ea7 100644 --- a/samples/AvaloniaSKPictureImageSample/MainWindow.axaml.cs +++ b/samples/AvaloniaSKPictureImageSample/MainWindow.axaml.cs @@ -9,9 +9,6 @@ public partial class MainWindow : Window public MainWindow() { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif } private void InitializeComponent() diff --git a/samples/AvaloniaSvgSample/MainWindow.axaml.cs b/samples/AvaloniaSvgSample/MainWindow.axaml.cs index e43538d58c..a4b479c216 100644 --- a/samples/AvaloniaSvgSample/MainWindow.axaml.cs +++ b/samples/AvaloniaSvgSample/MainWindow.axaml.cs @@ -5,6 +5,7 @@ using Avalonia.Input; using Avalonia.Markup.Xaml; using Avalonia.Media; +using Avalonia.Platform.Storage; using Avalonia.Svg; using ShimSkiaSharp; @@ -15,9 +16,6 @@ public partial class MainWindow : Window public MainWindow() { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif svgSvgDockPanel.AddHandler(DragDrop.DropEvent, Drop); svgSvgDockPanel.AddHandler(DragDrop.DragOverEvent, DragOver); @@ -89,7 +87,7 @@ private void DragOver(object sender, DragEventArgs e) { e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link); - if (!e.Data.Contains(DataFormats.Files)) + if (e.DataTransfer.TryGetFiles() is not { Length: > 0 }) { e.DragEffects = DragDropEffects.None; } @@ -97,41 +95,41 @@ private void DragOver(object sender, DragEventArgs e) private void Drop(object sender, DragEventArgs e) { - if (e.Data.Contains(DataFormats.Files)) + var fileName = e.DataTransfer.TryGetFiles()? + .Select(file => file.TryGetLocalPath()) + .FirstOrDefault(path => !string.IsNullOrWhiteSpace(path)); + + if (!string.IsNullOrWhiteSpace(fileName)) { - var fileName = e.Data.GetFileNames()?.FirstOrDefault(); - if (!string.IsNullOrWhiteSpace(fileName)) + if (sender == svgSvgDockPanel) { - if (sender == svgSvgDockPanel) - { - svgSvg.Path = fileName; - } - else if (sender == svgExtensionDockPanel) - { - svgExtensionImage.Source = new SvgImage - { - Source = SvgSource.Load(fileName, null) - }; - } - else if (sender == svgSourceDockPanel) + svgSvg.Path = fileName; + } + else if (sender == svgExtensionDockPanel) + { + svgExtensionImage.Source = new SvgImage { - svgSourceImage.Source = new SvgImage - { - Source = SvgSource.Load(fileName, null) - }; - } - else if (sender == svgResourceDockPanel) + Source = SvgSource.Load(fileName, null) + }; + } + else if (sender == svgSourceDockPanel) + { + svgSourceImage.Source = new SvgImage { - svgResourceImage.Source = new SvgImage - { - Source = SvgSource.Load(fileName, null) - }; - } - else if (sender == stringTextBox || sender == svgString) + Source = SvgSource.Load(fileName, null) + }; + } + else if (sender == svgResourceDockPanel) + { + svgResourceImage.Source = new SvgImage { - var source = File.ReadAllText(fileName); - stringTextBox.Text = source; - } + Source = SvgSource.Load(fileName, null) + }; + } + else if (sender == stringTextBox || sender == svgString) + { + var source = File.ReadAllText(fileName); + stringTextBox.Text = source; } } } diff --git a/samples/AvaloniaSvgSkiaSample/MainWindow.axaml.cs b/samples/AvaloniaSvgSkiaSample/MainWindow.axaml.cs index ad72030ba5..b4bd03831c 100644 --- a/samples/AvaloniaSvgSkiaSample/MainWindow.axaml.cs +++ b/samples/AvaloniaSvgSkiaSample/MainWindow.axaml.cs @@ -5,6 +5,7 @@ using Avalonia.Input; using Avalonia.Markup.Xaml; using Avalonia.Media; +using Avalonia.Platform.Storage; using Avalonia.Svg.Skia; using ShimSkiaSharp; @@ -15,9 +16,6 @@ public partial class MainWindow : Window public MainWindow() { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif svgSvgDockPanel.AddHandler(DragDrop.DropEvent, Drop); svgSvgDockPanel.AddHandler(DragDrop.DragOverEvent, DragOver); @@ -89,7 +87,7 @@ private void DragOver(object sender, DragEventArgs e) { e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link); - if (!e.Data.Contains(DataFormats.Files)) + if (e.DataTransfer.TryGetFiles() is not { Length: > 0 }) { e.DragEffects = DragDropEffects.None; } @@ -97,54 +95,54 @@ private void DragOver(object sender, DragEventArgs e) private void Drop(object sender, DragEventArgs e) { - if (e.Data.Contains(DataFormats.Files)) + var fileName = e.DataTransfer.TryGetFiles()? + .Select(file => file.TryGetLocalPath()) + .FirstOrDefault(path => !string.IsNullOrWhiteSpace(path)); + + if (!string.IsNullOrWhiteSpace(fileName)) { - var fileName = e.Data.GetFileNames()?.FirstOrDefault(); - if (!string.IsNullOrWhiteSpace(fileName)) + if (sender == svgSvgDockPanel) { - if (sender == svgSvgDockPanel) - { - svgSvg.Path = fileName; - } - else if (sender == svgExtensionDockPanel) + svgSvg.Path = fileName; + } + else if (sender == svgExtensionDockPanel) + { + var svg = SvgSource.Load(fileName); + if (svg is { }) { - var svg = SvgSource.Load(fileName); - if (svg is { }) + svgExtensionImage.Source = new SvgImage { - svgExtensionImage.Source = new SvgImage - { - Source = svg - }; - } + Source = svg + }; } - else if (sender == svgSourceDockPanel) + } + else if (sender == svgSourceDockPanel) + { + var svg = SvgSource.Load(fileName); + if (svg is { }) { - var svg = SvgSource.Load(fileName); - if (svg is { }) + svgSourceImage.Source = new SvgImage { - svgSourceImage.Source = new SvgImage - { - Source = svg - }; - } + Source = svg + }; } - else if (sender == svgResourceDockPanel) + } + else if (sender == svgResourceDockPanel) + { + var svg = SvgSource.Load(fileName); + if (svg is { }) { - var svg = SvgSource.Load(fileName); - if (svg is { }) + svgResourceImage.Source = new SvgImage { - svgResourceImage.Source = new SvgImage - { - Source = svg - }; - } - } - else if (sender == stringTextBox || sender == svgString) - { - var source = File.ReadAllText(fileName); - stringTextBox.Text = source; + Source = svg + }; } } + else if (sender == stringTextBox || sender == svgString) + { + var source = File.ReadAllText(fileName); + stringTextBox.Text = source; + } } } diff --git a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs index db7b1c4d5e..52f5fc8858 100644 --- a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs +++ b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs @@ -12,9 +12,6 @@ public partial class MainWindow : Window public MainWindow() { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif ApplySvgStyleButton.Click += ApplySvgStyleButtonClick; ApplySvgImageStyleButton.Click += ApplySvgImageStyleButtonClick; } diff --git a/src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs b/src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs index 219be2dd07..c6252722ee 100644 --- a/src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs +++ b/src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs @@ -14,6 +14,15 @@ namespace Avalonia.Svg; /// public class AvaloniaSvgAssetLoader : SM.ISvgAssetLoader { + private static float GetGlyphAdvance(GlyphTypeface glyphTypeface, int codepoint) + { + var glyphId = glyphTypeface.CharacterToGlyphMap.GetGlyph(codepoint); + + return glyphTypeface.TryGetHorizontalGlyphAdvance(glyphId, out var advance) + ? advance / (float)glyphTypeface.Metrics.DesignEmHeight + : 0f; + } + /// public SKImage LoadImage(Stream stream) { @@ -115,7 +124,7 @@ runningTypeface is not { } typeface } var glyphTypeface = (typeface ?? Typeface.Default).GlyphTypeface; - runningAdvance += glyphTypeface.GetGlyphAdvance(glyphTypeface.GetGlyph((uint)codepoint)); + runningAdvance += GetGlyphAdvance(glyphTypeface, codepoint); if (char.IsHighSurrogate(text[i])) { @@ -159,7 +168,7 @@ public float MeasureText(string? text, SKPaint paint, ref SKRect bounds) for (int i = 0; i < text.Length; i++) { var codepoint = char.ConvertToUtf32(text, i); - advance += glyphTypeface.GetGlyphAdvance(glyphTypeface.GetGlyph((uint)codepoint)); + advance += GetGlyphAdvance(glyphTypeface, codepoint); if (char.IsHighSurrogate(text[i])) { i++; diff --git a/src/Svg.Editor.Avalonia/InsertElementPickerView.axaml.cs b/src/Svg.Editor.Avalonia/InsertElementPickerView.axaml.cs index 4b400e4693..80c1b4267d 100644 --- a/src/Svg.Editor.Avalonia/InsertElementPickerView.axaml.cs +++ b/src/Svg.Editor.Avalonia/InsertElementPickerView.axaml.cs @@ -41,7 +41,7 @@ private void CancelButton_OnClick(object? sender, RoutedEventArgs e) Cancel(); } - private void ElementList_OnGotFocus(object? sender, GotFocusEventArgs e) + private void ElementList_OnGotFocus(object? sender, FocusChangedEventArgs e) { _elementList.IsDropDownOpen = true; } diff --git a/src/Svg.Editor.Avalonia/SymbolPickerView.axaml.cs b/src/Svg.Editor.Avalonia/SymbolPickerView.axaml.cs index a440eb5892..2e6e5b71d4 100644 --- a/src/Svg.Editor.Avalonia/SymbolPickerView.axaml.cs +++ b/src/Svg.Editor.Avalonia/SymbolPickerView.axaml.cs @@ -41,7 +41,7 @@ private void CancelButton_OnClick(object? sender, RoutedEventArgs e) Cancel(); } - private void SymbolList_OnGotFocus(object? sender, GotFocusEventArgs e) + private void SymbolList_OnGotFocus(object? sender, FocusChangedEventArgs e) { _symbolList.IsDropDownOpen = true; } diff --git a/src/Svg.Editor.Skia.Avalonia/SvgEditorWorkspace.axaml.cs b/src/Svg.Editor.Skia.Avalonia/SvgEditorWorkspace.axaml.cs index dde879fe95..4f2979e3e6 100644 --- a/src/Svg.Editor.Skia.Avalonia/SvgEditorWorkspace.axaml.cs +++ b/src/Svg.Editor.Skia.Avalonia/SvgEditorWorkspace.axaml.cs @@ -16,6 +16,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Platform; +using Avalonia.Platform.Storage; using Avalonia.Svg.Skia; using Avalonia.Threading; using Svg; @@ -39,6 +40,7 @@ namespace Svg.Editor.Skia.Avalonia; public partial class SvgEditorWorkspace : UserControl { private const string DefaultWorkspaceTitlePrefix = "SVG Editor"; + private static readonly DataFormat SvgNodeDragFormat = DataFormat.CreateStringApplicationFormat("SvgNode"); private struct DragInfo { @@ -695,21 +697,20 @@ private async void PlaceImageMenuItem_Click(object? sender, RoutedEventArgs e) private void Window_OnDragOver(object? sender, DragEventArgs e) { - if (e.Data.Contains(DataFormats.FileNames)) + if (e.DataTransfer.TryGetFiles()?.Any() == true) e.DragEffects = DragDropEffects.Copy; } private async void Window_OnDrop(object? sender, DragEventArgs e) { - if (e.Data.Contains(DataFormats.FileNames)) + var file = e.DataTransfer.TryGetFiles()? + .Select(x => x.TryGetLocalPath()) + .FirstOrDefault(x => !string.IsNullOrWhiteSpace(x)); + + if (!string.IsNullOrWhiteSpace(file)) { - var files = e.Data.GetFileNames(); - var file = files?.FirstOrDefault(); - if (!string.IsNullOrEmpty(file)) - { - LoadDocument(file!); - SvgView.InvalidateVisual(); - } + LoadDocument(file!); + SvgView.InvalidateVisual(); } } @@ -2036,7 +2037,7 @@ private void SaveExpandedNodes(TreeViewItem item, SvgNode node) ExpandedNodeIds.Add(node.Element.ID); for (var i = 0; i < node.Children.Count; i++) { - if (item.ItemContainerGenerator.ContainerFromIndex(i) is TreeViewItem child) + if (item.ContainerFromIndex(i) is TreeViewItem child) SaveExpandedNodes(child, node.Children[i]); } } @@ -2055,7 +2056,7 @@ private void RestoreExpandedNodes(TreeViewItem item, SvgNode node) item.IsExpanded = true; for (var i = 0; i < node.Children.Count; i++) { - if (item.ItemContainerGenerator.ContainerFromIndex(i) is TreeViewItem child) + if (item.ContainerFromIndex(i) is TreeViewItem child) RestoreExpandedNodes(child, node.Children[i]); } } @@ -3134,9 +3135,9 @@ private async void DocumentTree_OnPointerMoved(object? sender, PointerEventArgs return; _treeDragging = true; } - var data = new DataObject(); - data.Set("SvgNode", node); - await DragDrop.DoDragDrop(e, data, DragDropEffects.Move); + var data = new DataTransfer(); + data.Add(DataTransferItem.Create(SvgNodeDragFormat, node.Element.ID ?? string.Empty)); + await DragDrop.DoDragDropAsync(e, data, DragDropEffects.Move); _dragNode = null; _treeDragging = false; } @@ -3150,7 +3151,7 @@ private void DocumentTree_OnPointerReleased(object? sender, PointerReleasedEvent private void DocumentTree_OnDragOver(object? sender, DragEventArgs e) { - if (!e.Data.Contains("SvgNode")) + if (!e.DataTransfer.Contains(SvgNodeDragFormat)) { HideDropIndicator(); return; @@ -3206,62 +3207,59 @@ private void DocumentTree_OnDragLeave(object? sender, RoutedEventArgs e) private void DocumentTree_OnDrop(object? sender, DragEventArgs e) { - if (!e.Data.Contains("SvgNode") || _dropTarget is null) + if (!e.DataTransfer.Contains(SvgNodeDragFormat) || _dropTarget is null || _dragNode is not { } node) { HideDropIndicator(); return; } - if (e.Data.Get("SvgNode") is SvgNode node) + var target = _dropTarget; + if (node == target || IsAncestor(node, target) || node.Parent is null) { - var target = _dropTarget; - if (node == target || IsAncestor(node, target) || node.Parent is null) - { - HideDropIndicator(); - return; - } - - SaveUndoState(); - SaveExpandedNodes(); - node.Parent.Element.Children.Remove(node.Element); + HideDropIndicator(); + return; + } - switch (_dropPosition) - { - case DropPosition.Before: - if (target.Parent?.Element is SvgElement parentBefore) - { - var index = parentBefore.Children.IndexOf(target.Element); - if (index < 0) - index = parentBefore.Children.Count; - if (index >= parentBefore.Children.Count) - parentBefore.Children.Add(node.Element); - else - parentBefore.Children.Insert(index, node.Element); - } - break; - case DropPosition.After: - if (target.Parent?.Element is SvgElement parentAfter) - { - var index = parentAfter.Children.IndexOf(target.Element); - if (index < 0) - index = parentAfter.Children.Count - 1; - if (index + 1 >= parentAfter.Children.Count) - parentAfter.Children.Add(node.Element); - else - parentAfter.Children.Insert(index + 1, node.Element); - } - break; - default: - target.Element.Children.Add(node.Element); - break; - } + SaveUndoState(); + SaveExpandedNodes(); + node.Parent.Element.Children.Remove(node.Element); - SvgView.SkSvg!.FromSvgDocument(_document); - BuildTree(); - SelectNodeFromElement(node.Element); - SvgView.InvalidateVisual(); + switch (_dropPosition) + { + case DropPosition.Before: + if (target.Parent?.Element is SvgElement parentBefore) + { + var index = parentBefore.Children.IndexOf(target.Element); + if (index < 0) + index = parentBefore.Children.Count; + if (index >= parentBefore.Children.Count) + parentBefore.Children.Add(node.Element); + else + parentBefore.Children.Insert(index, node.Element); + } + break; + case DropPosition.After: + if (target.Parent?.Element is SvgElement parentAfter) + { + var index = parentAfter.Children.IndexOf(target.Element); + if (index < 0) + index = parentAfter.Children.Count - 1; + if (index + 1 >= parentAfter.Children.Count) + parentAfter.Children.Add(node.Element); + else + parentAfter.Children.Insert(index + 1, node.Element); + } + break; + default: + target.Element.Children.Add(node.Element); + break; } + SvgView.SkSvg!.FromSvgDocument(_document); + BuildTree(); + SelectNodeFromElement(node.Element); + SvgView.InvalidateVisual(); + HideDropIndicator(); _dropTarget = null; _dropPosition = DropPosition.None; diff --git a/src/Svg.Skia/SkiaModel.cs b/src/Svg.Skia/SkiaModel.cs index c652029ed6..809b9d901c 100644 --- a/src/Svg.Skia/SkiaModel.cs +++ b/src/Svg.Skia/SkiaModel.cs @@ -882,7 +882,7 @@ public SkiaSharp.SKColorChannel ToSKColorChannel(SKColorChannel colorChannel) ToSKImage(imageImageFilter.Image), ToSKRect(imageImageFilter.Src), ToSKRect(imageImageFilter.Dst), - SkiaSharp.SKFilterQuality.High); + SkiaSharp.SkiaExtensions.ToSamplingOptions(SkiaSharp.SKFilterQuality.High)); } case MatrixConvolutionImageFilter matrixConvolutionImageFilter: { From e5fa69bfb7f201a3cd42780f2ddd62ca70295ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 19 Mar 2026 23:14:09 +0100 Subject: [PATCH 05/14] Restore ReactiveUI.Avalonia in TestApp --- Svg.Skia.slnx | 2 +- ...tiveUI.props => ReactiveUI.Avalonia.props} | 2 +- samples/TestApp/Program.cs | 4 +-- samples/TestApp/Services/StorageService.cs | 7 +++-- samples/TestApp/TestApp.csproj | 4 ++- .../TestApp/ViewModels/MainWindowViewModel.cs | 31 +++++++++---------- samples/TestApp/Views/MainView.axaml.cs | 29 +++++++++-------- samples/TestApp/Views/MainWindow.axaml.cs | 3 -- 8 files changed, 42 insertions(+), 40 deletions(-) rename build/{Avalonia.ReactiveUI.props => ReactiveUI.Avalonia.props} (76%) diff --git a/Svg.Skia.slnx b/Svg.Skia.slnx index bb0c633335..532e7ab88b 100644 --- a/Svg.Skia.slnx +++ b/Svg.Skia.slnx @@ -30,7 +30,7 @@ - + diff --git a/build/Avalonia.ReactiveUI.props b/build/ReactiveUI.Avalonia.props similarity index 76% rename from build/Avalonia.ReactiveUI.props rename to build/ReactiveUI.Avalonia.props index e1419432c0..a3e8d07f54 100644 --- a/build/Avalonia.ReactiveUI.props +++ b/build/ReactiveUI.Avalonia.props @@ -1,6 +1,6 @@  - + diff --git a/samples/TestApp/Program.cs b/samples/TestApp/Program.cs index c6f59442c2..d1548bf369 100644 --- a/samples/TestApp/Program.cs +++ b/samples/TestApp/Program.cs @@ -1,7 +1,7 @@ using System; using Avalonia; -using Avalonia.ReactiveUI; using Avalonia.Svg.Skia; +using ReactiveUI.Avalonia; namespace TestApp; @@ -29,6 +29,6 @@ public static AppBuilder BuildAvaloniaApp() .With(new X11PlatformOptions { }) .LogToTrace() .UseSkia() - .UseReactiveUI(); + .UseReactiveUI(static _ => { }); } } diff --git a/samples/TestApp/Services/StorageService.cs b/samples/TestApp/Services/StorageService.cs index bb5797b4e5..9d4a4be5da 100644 --- a/samples/TestApp/Services/StorageService.cs +++ b/samples/TestApp/Services/StorageService.cs @@ -104,10 +104,11 @@ internal static class StorageService return window.StorageProvider; } - if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime { MainView: { } mainView }) + if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime { MainView: { } mainView } && + mainView is Visual visual) { - var visualRoot = mainView.GetVisualRoot(); - if (visualRoot is TopLevel topLevel) + var topLevel = TopLevel.GetTopLevel(visual); + if (topLevel is not null) { return topLevel.StorageProvider; } diff --git a/samples/TestApp/TestApp.csproj b/samples/TestApp/TestApp.csproj index 07d8b3d706..a3d70d7a00 100644 --- a/samples/TestApp/TestApp.csproj +++ b/samples/TestApp/TestApp.csproj @@ -16,6 +16,7 @@ 13 true + false @@ -26,7 +27,7 @@ - + @@ -43,6 +44,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/samples/TestApp/ViewModels/MainWindowViewModel.cs b/samples/TestApp/ViewModels/MainWindowViewModel.cs index 362061d8fc..d2027a32eb 100644 --- a/samples/TestApp/ViewModels/MainWindowViewModel.cs +++ b/samples/TestApp/ViewModels/MainWindowViewModel.cs @@ -8,9 +8,6 @@ using System.Text.Json; using System.Threading.Tasks; using System.Windows.Input; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform.Storage; using DynamicData; using DynamicData.Binding; @@ -81,13 +78,13 @@ public MainWindowViewModel() .ToObservableChangeSet() .Filter(queryFilter) .Sort(SortExpressionComparer.Ascending(x => x.Name)) - .ObserveOn(RxApp.MainThreadScheduler) + .ObserveOn(RxSchedulers.MainThreadScheduler) .Bind(out _filteredItems) .AsObservableList(); var resetQueryCanExecute = this.WhenAnyItemQuery() .Select(x => !string.IsNullOrWhiteSpace(x)) - .ObserveOn(RxApp.MainThreadScheduler); + .ObserveOn(RxSchedulers.MainThreadScheduler); ResetQueryCommand = ReactiveCommand.Create(() => ItemQuery = "", resetQueryCanExecute); @@ -141,26 +138,28 @@ private async Task ExportExecute(Avalonia.Svg.Skia.Svg svg) private async Task AddItemExecute() { - var window = (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow; - if (window is null) + var storageProvider = StorageService.GetStorageProvider(); + if (storageProvider is null) { return; } - var dlg = new OpenFileDialog + var result = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { AllowMultiple = true, - Filters = new List + Title = "Add svg files", + FileTypeFilter = new List { - new() {Name = "Svg Files (*.svg;*.svgz)", Extensions = new List {"svg", "svgz"}}, - new() {Name = "All Files (*.*)", Extensions = new List {"*"}} + StorageService.ImageSvg, + StorageService.ImageSvgz, + StorageService.All } - }; - var result = await dlg.ShowAsync(window); - if (result is { }) + }); + + foreach (var file in result) { - var paths = result.ToList(); - foreach (var path in paths) + var path = file.TryGetLocalPath(); + if (!string.IsNullOrWhiteSpace(path)) { AddItem(path); } diff --git a/samples/TestApp/Views/MainView.axaml.cs b/samples/TestApp/Views/MainView.axaml.cs index 4fe7790ad8..18e0b831fb 100644 --- a/samples/TestApp/Views/MainView.axaml.cs +++ b/samples/TestApp/Views/MainView.axaml.cs @@ -7,6 +7,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Platform.Storage; using ShimSkiaSharp; using SkiaSharp; using Svg.Model.Drawables; @@ -53,7 +54,7 @@ private void DragOver(object? sender, DragEventArgs e) { e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link); - if (!e.Data.Contains(DataFormats.Files)) + if (e.DataTransfer.TryGetFiles() is not { Length: > 0 }) { e.DragEffects = DragDropEffects.None; } @@ -61,21 +62,23 @@ private void DragOver(object? sender, DragEventArgs e) private void Drop(object? sender, DragEventArgs e) { - if (e.Data.Contains(DataFormats.Files)) + var paths = e.DataTransfer.TryGetFiles()? + .Select(file => file.TryGetLocalPath()) + .Where(path => !string.IsNullOrWhiteSpace(path)) + .Cast() + .ToArray(); + + if (paths is { Length: > 0 }) { - var paths = e.Data.GetFileNames(); - if (paths is { }) + if (DataContext is MainWindowViewModel vm) { - if (DataContext is MainWindowViewModel vm) + try + { + vm.Drop(paths); + } + catch (Exception) { - try - { - vm.Drop(paths); - } - catch (Exception) - { - // ignored - } + // ignored } } } diff --git a/samples/TestApp/Views/MainWindow.axaml.cs b/samples/TestApp/Views/MainWindow.axaml.cs index 8e4025db19..5101667d41 100644 --- a/samples/TestApp/Views/MainWindow.axaml.cs +++ b/samples/TestApp/Views/MainWindow.axaml.cs @@ -16,9 +16,6 @@ public partial class MainWindow : Window public MainWindow() { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif } private void InitializeComponent() From 10cda90eea7fe8bd653f2b8689146e142414da18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 19 Mar 2026 23:32:45 +0100 Subject: [PATCH 06/14] Fix docs build for Avalonia 12 APIs --- site/articles/reference/api-coverage-index.md | 8 +++++--- site/articles/reference/lunet-docs-pipeline.md | 3 ++- site/config.scriban | 10 +++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/site/articles/reference/api-coverage-index.md b/site/articles/reference/api-coverage-index.md index 6ebb979599..7d14fbc32b 100644 --- a/site/articles/reference/api-coverage-index.md +++ b/site/articles/reference/api-coverage-index.md @@ -25,10 +25,12 @@ The generated API reference under `/api` is built from these projects: Current API settings: - configuration: `Release` -- target framework override: `netstandard2.0` +- default target framework override: `netstandard2.0` +- Avalonia 12 project overrides: + `Svg.Controls.Avalonia`, `Svg.Controls.Skia.Avalonia`, `Skia.Controls.Avalonia`, `Svg.Editor.Avalonia`, and `Svg.Editor.Skia.Avalonia` build API metadata with `net8.0` - output path: `/api` -## Why `netstandard2.0` +## Why mixed target frameworks This repository mixes: @@ -36,7 +38,7 @@ This repository mixes: - multi-target editor packages, - `netstandard2.0`-only generator packages. -Using `netstandard2.0` as the documentation build target keeps the API site aligned across the documented assemblies without having to split the API generation into multiple passes. +The docs build keeps `netstandard2.0` as the default extraction target for the shared runtime and generator-facing packages, while overriding the Avalonia 12 packages to `net8.0`. That keeps a single API site without forcing the Avalonia projects back onto a framework they no longer target. ## `Svg.CodeGen.Skia` diff --git a/site/articles/reference/lunet-docs-pipeline.md b/site/articles/reference/lunet-docs-pipeline.md index 478bed5896..b4be763bab 100644 --- a/site/articles/reference/lunet-docs-pipeline.md +++ b/site/articles/reference/lunet-docs-pipeline.md @@ -26,7 +26,8 @@ API docs are generated by Lunet `api.dotnet` from the library projects listed in Current settings: - `Release` configuration -- `TargetFramework: netstandard2.0` +- default `TargetFramework: netstandard2.0` +- per-project `TargetFramework: net8.0` overrides for the Avalonia 12 packages - output under `/api` - Avalonia xrefs pointed at `https://api-docs.avaloniaui.net/docs` diff --git a/site/config.scriban b/site/config.scriban index 2245ef248c..b640aa6200 100644 --- a/site/config.scriban +++ b/site/config.scriban @@ -76,14 +76,14 @@ with api.dotnet { name: "Svg.Skia", path: "../src/Svg.Skia/Svg.Skia.csproj" }, { name: "Svg.Model", path: "../src/Svg.Model/Svg.Model.csproj" }, { name: "Svg.Custom", path: "../src/Svg.Custom/Svg.Custom.csproj" }, - { name: "Svg.Controls.Avalonia", path: "../src/Svg.Controls.Avalonia/Svg.Controls.Avalonia.csproj" }, - { name: "Svg.Controls.Skia.Avalonia", path: "../src/Svg.Controls.Skia.Avalonia/Svg.Controls.Skia.Avalonia.csproj" }, - { name: "Skia.Controls.Avalonia", path: "../src/Skia.Controls.Avalonia/Skia.Controls.Avalonia.csproj" }, + { name: "Svg.Controls.Avalonia", path: "../src/Svg.Controls.Avalonia/Svg.Controls.Avalonia.csproj", properties: { TargetFramework: "net8.0" } }, + { name: "Svg.Controls.Skia.Avalonia", path: "../src/Svg.Controls.Skia.Avalonia/Svg.Controls.Skia.Avalonia.csproj", properties: { TargetFramework: "net8.0" } }, + { name: "Skia.Controls.Avalonia", path: "../src/Skia.Controls.Avalonia/Skia.Controls.Avalonia.csproj", properties: { TargetFramework: "net8.0" } }, { name: "Svg.Editor.Core", path: "../src/Svg.Editor.Core/Svg.Editor.Core.csproj" }, { name: "Svg.Editor.Svg", path: "../src/Svg.Editor.Svg/Svg.Editor.Svg.csproj" }, { name: "Svg.Editor.Skia", path: "../src/Svg.Editor.Skia/Svg.Editor.Skia.csproj" }, - { name: "Svg.Editor.Avalonia", path: "../src/Svg.Editor.Avalonia/Svg.Editor.Avalonia.csproj" }, - { name: "Svg.Editor.Skia.Avalonia", path: "../src/Svg.Editor.Skia.Avalonia/Svg.Editor.Skia.Avalonia.csproj" }, + { name: "Svg.Editor.Avalonia", path: "../src/Svg.Editor.Avalonia/Svg.Editor.Avalonia.csproj", properties: { TargetFramework: "net8.0" } }, + { name: "Svg.Editor.Skia.Avalonia", path: "../src/Svg.Editor.Skia.Avalonia/Svg.Editor.Skia.Avalonia.csproj", properties: { TargetFramework: "net8.0" } }, { name: "ShimSkiaSharp", path: "../src/ShimSkiaSharp/ShimSkiaSharp.csproj" }, { name: "Svg.SourceGenerator.Skia", path: "../src/Svg.SourceGenerator.Skia/Svg.SourceGenerator.Skia.csproj" } ] From 1cbc3e21f964071ea13939d1a19a9214cefd6bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Thu, 19 Mar 2026 23:34:26 +0100 Subject: [PATCH 07/14] Skip docs deploy job on pull requests --- .github/workflows/docs.yml | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 487a2555d4..1f2fec6450 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,10 +11,10 @@ on: - main permissions: - contents: write + contents: read jobs: - deploy: + build: runs-on: ubuntu-latest steps: - name: Checkout @@ -37,8 +37,31 @@ jobs: chmod +x build-docs.sh ./build-docs.sh - - name: Deploy to GitHub Pages + - name: Upload docs artifact if: github.event_name == 'push' + uses: actions/upload-artifact@v4 + with: + name: docs-site + path: ./site/.lunet/build/www + if-no-files-found: error + + deploy: + if: github.event_name == 'push' + needs: build + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download docs artifact + uses: actions/download-artifact@v4 + with: + name: docs-site + path: ./site/.lunet/build/www + + - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} From 784f10b9f74cb35512a77fc2b68ac343f9b9d18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 8 Apr 2026 22:29:34 +0200 Subject: [PATCH 08/14] Use ProDiagnostics for devtools --- Directory.Packages.props | 2 +- build/Avalonia.Diagnostics.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 04c6c70302..324e188354 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,7 +6,7 @@ - + diff --git a/build/Avalonia.Diagnostics.props b/build/Avalonia.Diagnostics.props index 90df8e4d8f..814bd29a4a 100644 --- a/build/Avalonia.Diagnostics.props +++ b/build/Avalonia.Diagnostics.props @@ -4,6 +4,6 @@ false - + From 1aaaa347d04eb6c579a3b71f942438a090ad8149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 8 Apr 2026 22:38:03 +0200 Subject: [PATCH 09/14] Always reference ProDiagnostics --- build/Avalonia.Diagnostics.props | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/build/Avalonia.Diagnostics.props b/build/Avalonia.Diagnostics.props index 814bd29a4a..dd058c6baa 100644 --- a/build/Avalonia.Diagnostics.props +++ b/build/Avalonia.Diagnostics.props @@ -1,9 +1,6 @@  - - false - - + From 54946c9e1bf3c9787fba136bfdebe5e9ccf8239d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 8 Apr 2026 22:38:07 +0200 Subject: [PATCH 10/14] Add NuGet package badge table --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 049b6d4b66..aa1938c8a0 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,29 @@ [![Github All Releases](https://img.shields.io/github/downloads/wieslawsoltes/svg.skia/total.svg)](https://github.com/wieslawsoltes/svg.skia) [![Github Releases](https://img.shields.io/github/downloads/wieslawsoltes/svg.skia/latest/total.svg)](https://github.com/wieslawsoltes/svg.skia) +| Package ID | NuGet | Downloads | +| --- | --- | --- | +| `ShimSkiaSharp` | [![NuGet](https://img.shields.io/nuget/v/ShimSkiaSharp.svg)](https://www.nuget.org/packages/ShimSkiaSharp/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/ShimSkiaSharp.svg)](https://www.nuget.org/packages/ShimSkiaSharp/) | +| `Skia.Controls.Avalonia` | [![NuGet](https://img.shields.io/nuget/v/Skia.Controls.Avalonia.svg)](https://www.nuget.org/packages/Skia.Controls.Avalonia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Skia.Controls.Avalonia.svg)](https://www.nuget.org/packages/Skia.Controls.Avalonia/) | +| `Svg.Animation` | [![NuGet](https://img.shields.io/nuget/v/Svg.Animation.svg)](https://www.nuget.org/packages/Svg.Animation/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Animation.svg)](https://www.nuget.org/packages/Svg.Animation/) | +| `Svg.CodeGen.Skia` | [![NuGet](https://img.shields.io/nuget/v/Svg.CodeGen.Skia.svg)](https://www.nuget.org/packages/Svg.CodeGen.Skia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.CodeGen.Skia.svg)](https://www.nuget.org/packages/Svg.CodeGen.Skia/) | +| `Svg.Controls.Avalonia` | [![NuGet](https://img.shields.io/nuget/v/Svg.Controls.Avalonia.svg)](https://www.nuget.org/packages/Svg.Controls.Avalonia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Controls.Avalonia.svg)](https://www.nuget.org/packages/Svg.Controls.Avalonia/) | +| `Svg.Controls.Skia.Avalonia` | [![NuGet](https://img.shields.io/nuget/v/Svg.Controls.Skia.Avalonia.svg)](https://www.nuget.org/packages/Svg.Controls.Skia.Avalonia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Controls.Skia.Avalonia.svg)](https://www.nuget.org/packages/Svg.Controls.Skia.Avalonia/) | +| `Svg.Controls.Skia.Uno` | [![NuGet](https://img.shields.io/nuget/v/Svg.Controls.Skia.Uno.svg)](https://www.nuget.org/packages/Svg.Controls.Skia.Uno/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Controls.Skia.Uno.svg)](https://www.nuget.org/packages/Svg.Controls.Skia.Uno/) | +| `Svg.Custom` | [![NuGet](https://img.shields.io/nuget/v/Svg.Custom.svg)](https://www.nuget.org/packages/Svg.Custom/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Custom.svg)](https://www.nuget.org/packages/Svg.Custom/) | +| `Svg.Editor.Avalonia` | [![NuGet](https://img.shields.io/nuget/v/Svg.Editor.Avalonia.svg)](https://www.nuget.org/packages/Svg.Editor.Avalonia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Editor.Avalonia.svg)](https://www.nuget.org/packages/Svg.Editor.Avalonia/) | +| `Svg.Editor.Core` | [![NuGet](https://img.shields.io/nuget/v/Svg.Editor.Core.svg)](https://www.nuget.org/packages/Svg.Editor.Core/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Editor.Core.svg)](https://www.nuget.org/packages/Svg.Editor.Core/) | +| `Svg.Editor.Skia` | [![NuGet](https://img.shields.io/nuget/v/Svg.Editor.Skia.svg)](https://www.nuget.org/packages/Svg.Editor.Skia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Editor.Skia.svg)](https://www.nuget.org/packages/Svg.Editor.Skia/) | +| `Svg.Editor.Skia.Avalonia` | [![NuGet](https://img.shields.io/nuget/v/Svg.Editor.Skia.Avalonia.svg)](https://www.nuget.org/packages/Svg.Editor.Skia.Avalonia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Editor.Skia.Avalonia.svg)](https://www.nuget.org/packages/Svg.Editor.Skia.Avalonia/) | +| `Svg.Editor.Svg` | [![NuGet](https://img.shields.io/nuget/v/Svg.Editor.Svg.svg)](https://www.nuget.org/packages/Svg.Editor.Svg/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Editor.Svg.svg)](https://www.nuget.org/packages/Svg.Editor.Svg/) | +| `Svg.Model` | [![NuGet](https://img.shields.io/nuget/v/Svg.Model.svg)](https://www.nuget.org/packages/Svg.Model/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Model.svg)](https://www.nuget.org/packages/Svg.Model/) | +| `Svg.SceneGraph` | [![NuGet](https://img.shields.io/nuget/v/Svg.SceneGraph.svg)](https://www.nuget.org/packages/Svg.SceneGraph/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.SceneGraph.svg)](https://www.nuget.org/packages/Svg.SceneGraph/) | +| `Svg.Skia` | [![NuGet](https://img.shields.io/nuget/v/Svg.Skia.svg)](https://www.nuget.org/packages/Svg.Skia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Skia.svg)](https://www.nuget.org/packages/Svg.Skia/) | +| `Svg.Skia.Converter` | [![NuGet](https://img.shields.io/nuget/v/Svg.Skia.Converter.svg)](https://www.nuget.org/packages/Svg.Skia.Converter/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.Skia.Converter.svg)](https://www.nuget.org/packages/Svg.Skia.Converter/) | +| `Svg.SourceGenerator.Skia` | [![NuGet](https://img.shields.io/nuget/v/Svg.SourceGenerator.Skia.svg)](https://www.nuget.org/packages/Svg.SourceGenerator.Skia/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Svg.SourceGenerator.Skia.svg)](https://www.nuget.org/packages/Svg.SourceGenerator.Skia/) | +| `svgc` | [![NuGet](https://img.shields.io/nuget/v/svgc.svg)](https://www.nuget.org/packages/svgc/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/svgc.svg)](https://www.nuget.org/packages/svgc/) | +| `SvgToPng` | [![NuGet](https://img.shields.io/nuget/v/SvgToPng.svg)](https://www.nuget.org/packages/SvgToPng/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/SvgToPng.svg)](https://www.nuget.org/packages/SvgToPng/) | + *Svg.Skia* is an [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) rendering library. ## About From 4a5f1b5cfd60cf6453b86bbaf470ad351b7a0c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 8 Apr 2026 22:39:22 +0200 Subject: [PATCH 11/14] Update Directory.Build.props --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 7119ef9e2f..2b622bcb30 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ 12.0.0 $(VersionSuffix) - 6.5.31 + 6.5.31.1 $(VersionSuffix) Wiesław Šoltés Wiesław Šoltés From 35846d8d25bf6a46b43caed24971132796189fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 8 Apr 2026 22:48:14 +0200 Subject: [PATCH 12/14] Include hidden docs artifact files --- .github/workflows/docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1f2fec6450..3bf21b474d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -43,6 +43,7 @@ jobs: with: name: docs-site path: ./site/.lunet/build/www + include-hidden-files: true if-no-files-found: error deploy: From 3b0b25b409bc32ddf253af5ed9a6e50e29c4b3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 8 Apr 2026 23:07:40 +0200 Subject: [PATCH 13/14] Tighten docs workflow deploy --- .github/workflows/docs.yml | 8 ++++++-- site/articles/reference/lunet-docs-pipeline.md | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3bf21b474d..96251b1685 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,6 +10,10 @@ on: - master - main +concurrency: + group: docs-${{ github.ref }} + cancel-in-progress: true + permissions: contents: read @@ -38,7 +42,7 @@ jobs: ./build-docs.sh - name: Upload docs artifact - if: github.event_name == 'push' + if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch uses: actions/upload-artifact@v4 with: name: docs-site @@ -47,7 +51,7 @@ jobs: if-no-files-found: error deploy: - if: github.event_name == 'push' + if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch needs: build permissions: contents: write diff --git a/site/articles/reference/lunet-docs-pipeline.md b/site/articles/reference/lunet-docs-pipeline.md index b4be763bab..539124aa93 100644 --- a/site/articles/reference/lunet-docs-pipeline.md +++ b/site/articles/reference/lunet-docs-pipeline.md @@ -83,4 +83,4 @@ All commands operate in `site/` and publish to `site/.lunet/build/www`. ## CI publishing -`.github/workflows/docs.yml` restores the local Lunet tool, builds the site, and deploys `site/.lunet/build/www` to GitHub Pages on pushes to `main` or `master`. +`.github/workflows/docs.yml` restores the local Lunet tool, builds the site for pull requests and pushes to `master` or `main`, and deploys `site/.lunet/build/www` to GitHub Pages only from the repository's default branch. From 46b91b69ac07cb7fbf3658127aeba69acc504f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 8 Apr 2026 23:08:21 +0200 Subject: [PATCH 14/14] Restore sample devtools hooks --- samples/AvalonDraw/App.axaml.cs | 4 ++++ samples/AvaloniaControlsSample/App.axaml.cs | 4 ++++ samples/AvaloniaSKPictureImageSample/App.axaml.cs | 4 ++++ samples/AvaloniaSvgSample/App.axaml.cs | 4 ++++ samples/AvaloniaSvgSkiaSample/App.axaml.cs | 4 ++++ samples/AvaloniaSvgSkiaStylingSample/App.axaml.cs | 4 ++++ samples/TestApp/App.axaml.cs | 4 ++++ 7 files changed, 28 insertions(+) diff --git a/samples/AvalonDraw/App.axaml.cs b/samples/AvalonDraw/App.axaml.cs index dc10e882f7..93c8c2dfc4 100644 --- a/samples/AvalonDraw/App.axaml.cs +++ b/samples/AvalonDraw/App.axaml.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Diagnostics; using Avalonia.Markup.Xaml; namespace AvalonDraw; @@ -19,5 +20,8 @@ public override void OnFrameworkInitializationCompleted() } base.OnFrameworkInitializationCompleted(); +#if DEBUG + this.AttachDevTools(); +#endif } } diff --git a/samples/AvaloniaControlsSample/App.axaml.cs b/samples/AvaloniaControlsSample/App.axaml.cs index 06310375c7..e200fda087 100644 --- a/samples/AvaloniaControlsSample/App.axaml.cs +++ b/samples/AvaloniaControlsSample/App.axaml.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Diagnostics; using Avalonia.Markup.Xaml; namespace AvaloniaControlsSample; @@ -19,5 +20,8 @@ public override void OnFrameworkInitializationCompleted() } base.OnFrameworkInitializationCompleted(); +#if DEBUG + this.AttachDevTools(); +#endif } } diff --git a/samples/AvaloniaSKPictureImageSample/App.axaml.cs b/samples/AvaloniaSKPictureImageSample/App.axaml.cs index 2855c249e0..476d5c5590 100644 --- a/samples/AvaloniaSKPictureImageSample/App.axaml.cs +++ b/samples/AvaloniaSKPictureImageSample/App.axaml.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Diagnostics; using Avalonia.Markup.Xaml; namespace AvaloniaSKPictureImageSample; @@ -19,5 +20,8 @@ public override void OnFrameworkInitializationCompleted() } base.OnFrameworkInitializationCompleted(); +#if DEBUG + this.AttachDevTools(); +#endif } } diff --git a/samples/AvaloniaSvgSample/App.axaml.cs b/samples/AvaloniaSvgSample/App.axaml.cs index 81f4980ffd..9122219dfe 100644 --- a/samples/AvaloniaSvgSample/App.axaml.cs +++ b/samples/AvaloniaSvgSample/App.axaml.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Diagnostics; using Avalonia.Markup.Xaml; namespace AvaloniaSvgSample; @@ -19,5 +20,8 @@ public override void OnFrameworkInitializationCompleted() } base.OnFrameworkInitializationCompleted(); +#if DEBUG + this.AttachDevTools(); +#endif } } diff --git a/samples/AvaloniaSvgSkiaSample/App.axaml.cs b/samples/AvaloniaSvgSkiaSample/App.axaml.cs index c50c7e0b4c..f77ff6ab2d 100644 --- a/samples/AvaloniaSvgSkiaSample/App.axaml.cs +++ b/samples/AvaloniaSvgSkiaSample/App.axaml.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Diagnostics; using Avalonia.Markup.Xaml; namespace AvaloniaSvgSkiaSample; @@ -19,5 +20,8 @@ public override void OnFrameworkInitializationCompleted() } base.OnFrameworkInitializationCompleted(); +#if DEBUG + this.AttachDevTools(); +#endif } } diff --git a/samples/AvaloniaSvgSkiaStylingSample/App.axaml.cs b/samples/AvaloniaSvgSkiaStylingSample/App.axaml.cs index 0c33b33fe6..b094a37b67 100644 --- a/samples/AvaloniaSvgSkiaStylingSample/App.axaml.cs +++ b/samples/AvaloniaSvgSkiaStylingSample/App.axaml.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Diagnostics; using Avalonia.Markup.Xaml; namespace AvaloniaSvgSkiaStylingSample; @@ -19,5 +20,8 @@ public override void OnFrameworkInitializationCompleted() } base.OnFrameworkInitializationCompleted(); +#if DEBUG + this.AttachDevTools(); +#endif } } diff --git a/samples/TestApp/App.axaml.cs b/samples/TestApp/App.axaml.cs index 1a4bb0432c..96699bc176 100644 --- a/samples/TestApp/App.axaml.cs +++ b/samples/TestApp/App.axaml.cs @@ -3,6 +3,7 @@ using System.IO; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Diagnostics; using Avalonia.Markup.Xaml; using TestApp.Services; using TestApp.ViewModels; @@ -79,5 +80,8 @@ public override void OnFrameworkInitializationCompleted() } base.OnFrameworkInitializationCompleted(); +#if DEBUG + this.AttachDevTools(); +#endif } }