diff --git a/.gitmodules b/.gitmodules
index 032bc879cc..6e9f2f7c14 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,6 @@
[submodule "nukebuild/il-repack"]
path = nukebuild/il-repack
url = https://github.com/Gillibald/il-repack
+[submodule "external/Tmds.DBus.SourceGenerator"]
+ path = external/Tmds.DBus.SourceGenerator
+ url = https://github.com/jmacato/Tmds.DBus.SourceGenerator.git
diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf
index 6089a06d4f..4b964448df 100644
--- a/Avalonia.Desktop.slnf
+++ b/Avalonia.Desktop.slnf
@@ -2,6 +2,7 @@
"solution": {
"path": "Avalonia.sln",
"projects": [
+ "external\\Tmds.DBus.SourceGenerator\\Tmds.DBus.SourceGenerator\\Tmds.DBus.SourceGenerator.csproj",
"packages\\Avalonia\\Avalonia.csproj",
"samples\\AppWithoutLifetime\\AppWithoutLifetime.csproj",
"samples\\ControlCatalog.NetCore\\ControlCatalog.NetCore.csproj",
@@ -40,13 +41,13 @@
"src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
"src\\Markup\\Avalonia.Markup\\Avalonia.Markup.csproj",
"src\\Skia\\Avalonia.Skia\\Avalonia.Skia.csproj",
- "src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj",
- "src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj",
- "src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",
"src\\tools\\Avalonia.Analyzers\\Avalonia.Analyzers.csproj",
"src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
"src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
"src\\tools\\DevGenerators\\DevGenerators.csproj",
+ "src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj",
+ "src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj",
+ "src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",
"tests\\Avalonia.Base.UnitTests\\Avalonia.Base.UnitTests.csproj",
"tests\\Avalonia.Benchmarks\\Avalonia.Benchmarks.csproj",
"tests\\Avalonia.Controls.DataGrid.UnitTests\\Avalonia.Controls.DataGrid.UnitTests.csproj",
diff --git a/Avalonia.sln b/Avalonia.sln
index 9c78c109a5..119b7ab197 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -301,6 +301,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildTasks", "BuildTasks",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PInvoke", "tests\TestFiles\BuildTasks\PInvoke\PInvoke.csproj", "{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tmds.DBus.SourceGenerator", "external\Tmds.DBus.SourceGenerator\Tmds.DBus.SourceGenerator\Tmds.DBus.SourceGenerator.csproj", "{068247A8-21E8-40D2-83CF-8758410FACAD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -710,6 +712,10 @@ Global
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {068247A8-21E8-40D2-83CF-8758410FACAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {068247A8-21E8-40D2-83CF-8758410FACAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {068247A8-21E8-40D2-83CF-8758410FACAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {068247A8-21E8-40D2-83CF-8758410FACAD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -798,6 +804,7 @@ Global
{9D6AEF22-221F-4F4B-B335-A4BA510F002C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{5BF0C3B8-E595-4940-AB30-2DA206C2F085} = {9D6AEF22-221F-4F4B-B335-A4BA510F002C}
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4} = {5BF0C3B8-E595-4940-AB30-2DA206C2F085}
+ {068247A8-21E8-40D2-83CF-8758410FACAD} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/external/Tmds.DBus.SourceGenerator b/external/Tmds.DBus.SourceGenerator
new file mode 160000
index 0000000000..3b334c4fbc
--- /dev/null
+++ b/external/Tmds.DBus.SourceGenerator
@@ -0,0 +1 @@
+Subproject commit 3b334c4fbce091fc16a812be134a0ea5d2ed8232
diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
index 143db003cd..c92eaf3e0e 100644
--- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
@@ -222,6 +222,7 @@ namespace ControlCatalog.Pages
{
Title = "Open file",
FileTypeFilter = GetFileTypes(),
+ SuggestedFileName = "FileName",
SuggestedStartLocation = lastSelectedDirectory,
AllowMultiple = openMultiple.IsChecked == true
});
@@ -264,6 +265,7 @@ namespace ControlCatalog.Pages
{
Title = "Folder file",
SuggestedStartLocation = lastSelectedDirectory,
+ SuggestedFileName = "FileName",
AllowMultiple = openMultiple.IsChecked == true
});
diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs
index 3d3cd2995d..c120f9ee77 100644
--- a/src/Android/Avalonia.Android/AvaloniaView.cs
+++ b/src/Android/Avalonia.Android/AvaloniaView.cs
@@ -42,6 +42,13 @@ namespace Avalonia.Android
set { _root.Content = value; }
}
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ _root?.Dispose();
+ _root = null;
+ }
+
public override bool DispatchKeyEvent(KeyEvent e)
{
return _view.View.DispatchKeyEvent(e);
diff --git a/src/Android/Avalonia.Android/SingleViewLifetime.cs b/src/Android/Avalonia.Android/SingleViewLifetime.cs
index f8a2ee2894..2b6b4d3359 100644
--- a/src/Android/Avalonia.Android/SingleViewLifetime.cs
+++ b/src/Android/Avalonia.Android/SingleViewLifetime.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Android
_activity = activity;
if (activity is IAvaloniaActivity activableActivity)
- {
+ {
activableActivity.Activated += (_, args) => Activated?.Invoke(this, args);
activableActivity.Deactivated += (_, args) => Deactivated?.Invoke(this, args);
}
diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
index 69bf8cae5f..daffe0ce77 100644
--- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
+++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
@@ -21,6 +21,7 @@ namespace Avalonia.Input.GestureRecognizers
private int _gestureId;
private Point _pointerPressedPoint;
private VelocityTracker? _velocityTracker;
+ private Visual? _rootTarget;
// Movement per second
private Vector _inertia;
@@ -99,7 +100,8 @@ namespace Avalonia.Input.GestureRecognizers
EndGesture();
_tracking = e.Pointer;
_gestureId = ScrollGestureEventArgs.GetNextFreeId();
- _trackedRootPoint = _pointerPressedPoint = e.GetPosition((Visual?)Target);
+ _rootTarget = (Visual?)(Target as Visual)?.VisualRoot;
+ _trackedRootPoint = _pointerPressedPoint = e.GetPosition(_rootTarget);
}
}
@@ -107,7 +109,7 @@ namespace Avalonia.Input.GestureRecognizers
{
if (e.Pointer == _tracking)
{
- var rootPoint = e.GetPosition((Visual?)Target);
+ var rootPoint = e.GetPosition(_rootTarget);
if (!_scrolling)
{
if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance)
@@ -134,8 +136,8 @@ namespace Avalonia.Input.GestureRecognizers
_velocityTracker?.AddPosition(TimeSpan.FromMilliseconds(e.Timestamp), _pointerPressedPoint - rootPoint);
_lastMoveTimestamp = e.Timestamp;
- _trackedRootPoint = rootPoint;
Target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, vector));
+ _trackedRootPoint = rootPoint;
e.Handled = true;
}
}
@@ -156,6 +158,7 @@ namespace Avalonia.Input.GestureRecognizers
Target!.RaiseEvent(new ScrollGestureEndedEventArgs(_gestureId));
_gestureId = 0;
_lastMoveTimestamp = null;
+ _rootTarget = null;
}
}
diff --git a/src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs b/src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs
index fa4fccd47a..267ba59c71 100644
--- a/src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs
+++ b/src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs
@@ -7,11 +7,6 @@ namespace Avalonia.Platform.Storage;
///
public class FilePickerSaveOptions : PickerOptions
{
- ///
- /// Gets or sets the file name that the file save picker suggests to the user.
- ///
- public string? SuggestedFileName { get; set; }
-
///
/// Gets or sets the default extension to be used to save the file.
///
diff --git a/src/Avalonia.Base/Platform/Storage/PickerOptions.cs b/src/Avalonia.Base/Platform/Storage/PickerOptions.cs
index 07f99f32c8..4fcc85a07a 100644
--- a/src/Avalonia.Base/Platform/Storage/PickerOptions.cs
+++ b/src/Avalonia.Base/Platform/Storage/PickerOptions.cs
@@ -1,4 +1,7 @@
-namespace Avalonia.Platform.Storage;
+using System.Collections.Generic;
+using Avalonia.Platform.Storage;
+
+namespace Avalonia.Platform.Storage;
///
/// Common options for , and methods.
@@ -16,4 +19,9 @@ public class PickerOptions
/// or .
///
public IStorageFolder? SuggestedStartLocation { get; set; }
+
+ ///
+ /// Gets or sets the file name that the file picker suggests to the user.
+ ///
+ public string? SuggestedFileName { get; set; }
}
diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs
index 9774468e16..85dc082ff6 100644
--- a/src/Avalonia.Base/Visual.cs
+++ b/src/Avalonia.Base/Visual.cs
@@ -195,23 +195,32 @@ namespace Avalonia
///
/// Gets a value indicating whether this control and all its parents are visible.
///
- public bool IsEffectivelyVisible
+ public bool IsEffectivelyVisible { get; private set; } = true;
+
+ ///
+ /// Updates the property based on the parent's
+ /// .
+ ///
+ /// The effective visibility of the parent control.
+ private void UpdateIsEffectivelyVisible(bool parentState)
{
- get
- {
- Visual? node = this;
+ var isEffectivelyVisible = parentState && IsVisible;
- while (node != null)
- {
- if (!node.IsVisible)
- {
- return false;
- }
+ if (IsEffectivelyVisible == isEffectivelyVisible)
+ return;
- node = node.VisualParent;
- }
+ IsEffectivelyVisible = isEffectivelyVisible;
- return true;
+ // PERF-SENSITIVE: This is called on entire hierarchy and using foreach or LINQ
+ // will cause extra allocations and overhead.
+
+ var children = VisualChildren;
+
+ // ReSharper disable once ForCanBeConvertedToForeach
+ for (int i = 0; i < children.Count; ++i)
+ {
+ var child = children[i];
+ child.UpdateIsEffectivelyVisible(isEffectivelyVisible);
}
}
@@ -453,7 +462,11 @@ namespace Avalonia
{
base.OnPropertyChanged(change);
- if (change.Property == FlowDirectionProperty)
+ if (change.Property == IsVisibleProperty)
+ {
+ UpdateIsEffectivelyVisible(VisualParent?.IsEffectivelyVisible ?? true);
+ }
+ else if (change.Property == FlowDirectionProperty)
{
InvalidateMirrorTransform();
@@ -463,7 +476,7 @@ namespace Avalonia
}
}
}
-
+
protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
base.LogicalChildrenCollectionChanged(sender, e);
@@ -492,14 +505,16 @@ namespace Avalonia
AttachToCompositor(compositingRenderer.Compositor);
}
InvalidateMirrorTransform();
+ UpdateIsEffectivelyVisible(_visualParent!.IsEffectivelyVisible);
OnAttachedToVisualTree(e);
AttachedToVisualTree?.Invoke(this, e);
InvalidateVisual();
+
_visualRoot.Renderer.RecalculateChildren(_visualParent!);
-
- if (ZIndex != 0 && VisualParent is Visual parent)
- parent.HasNonUniformZIndexChildren = true;
-
+
+ if (ZIndex != 0 && _visualParent is { })
+ _visualParent.HasNonUniformZIndexChildren = true;
+
var visualChildren = VisualChildren;
var visualChildrenCount = visualChildren.Count;
@@ -529,6 +544,7 @@ namespace Avalonia
}
DisableTransitions();
+ UpdateIsEffectivelyVisible(true);
OnDetachedFromVisualTree(e);
DetachFromCompositor();
diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
index 880bbb7340..9a2f98f771 100644
--- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
+++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
@@ -51,6 +51,11 @@ namespace Avalonia.Controls.Embedding
}
protected override Type StyleKeyOverride => typeof(EmbeddableControlRoot);
- public void Dispose() => PlatformImpl?.Dispose();
+
+ public void Dispose()
+ {
+ PlatformImpl?.Dispose();
+ LayoutManager?.Dispose();
+ }
}
}
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index cbda1dfbe6..01d67582d5 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -672,9 +672,12 @@ namespace Avalonia.Controls
{
_textLayout?.Dispose();
_textLayout = null;
+
+ VisualChildren.Clear();
- InvalidateVisual();
+ _textRuns = null;
+ InvalidateVisual();
InvalidateMeasure();
}
@@ -691,8 +694,6 @@ namespace Avalonia.Controls
if (HasComplexContent)
{
- VisualChildren.Clear();
-
var textRuns = new List();
foreach (var inline in inlines!)
@@ -876,9 +877,9 @@ namespace Avalonia.Controls
}
}
-#pragma warning disable CA1815 // Equals und Gleichheitsoperator für Werttypen außer Kraft setzen
+#pragma warning disable CA1815
protected readonly struct InlinesTextSource : ITextSource
-#pragma warning restore CA1815 // Equals und Gleichheitsoperator für Werttypen außer Kraft setzen
+#pragma warning restore CA1815
{
private readonly IReadOnlyList _textRuns;
private readonly IReadOnlyList>? _textModifier;
diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index 431be00f4e..8009492d77 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/src/Avalonia.Controls/TextBox.cs
@@ -124,6 +124,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty MaxLinesProperty =
AvaloniaProperty.Register(nameof(MaxLines));
+ ///
+ /// Defines the property
+ ///
+ public static readonly StyledProperty MinLinesProperty =
+ AvaloniaProperty.Register(nameof(MinLines));
+
///
/// Defines the property
///
@@ -519,6 +525,15 @@ namespace Avalonia.Controls
set => SetValue(MaxLinesProperty, value);
}
+ ///
+ /// Gets or sets the minimum number of visible lines to size to.
+ ///
+ public int MinLines
+ {
+ get => GetValue(MinLinesProperty);
+ set => SetValue(MinLinesProperty, value);
+ }
+
///
/// Gets or sets the spacing between characters
///
@@ -913,6 +928,10 @@ namespace Avalonia.Controls
{
InvalidateMeasure();
}
+ else if (change.Property == MinLinesProperty)
+ {
+ InvalidateMeasure();
+ }
else if (change.Property == UndoLimitProperty)
{
OnUndoLimitChanged(change.GetNewValue());
@@ -1836,7 +1855,7 @@ namespace Avalonia.Controls
}
SetCurrentValue(SelectionEndProperty, SelectionEnd + offset);
-
+
if (moveCaretPosition)
{
_presenter.MoveCaretToTextPosition(SelectionEnd);
@@ -2034,7 +2053,7 @@ namespace Avalonia.Controls
var margin = visual.GetValue(Layoutable.MarginProperty);
var padding = visual.GetValue(Decorator.PaddingProperty);
-
+
verticalSpace += margin.Top + padding.Top + padding.Bottom + margin.Bottom;
visual = visual.VisualParent;
@@ -2073,8 +2092,8 @@ namespace Avalonia.Controls
var selectionStart = CaretIndex;
MoveHorizontal(-1, true, false, false);
-
- if (SelectionEnd > 0 &&
+
+ if (SelectionEnd > 0 &&
selectionStart < text.Length && text[selectionStart] == ' ')
{
SetCurrentValue(SelectionEndProperty, SelectionEnd - 1);
@@ -2203,30 +2222,46 @@ namespace Avalonia.Controls
var fontSize = FontSize;
var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
var paragraphProperties = TextLayout.CreateTextParagraphProperties(typeface, fontSize, null, default, default, null, default, LineHeight, default);
- var textLayout = new TextLayout(new MaxLinesTextSource(MaxLines), paragraphProperties);
+ var textLayout = new TextLayout(new LineTextSource(MaxLines), paragraphProperties);
var verticalSpace = GetVerticalSpaceBetweenScrollViewerAndPresenter();
maxHeight = Math.Ceiling(textLayout.Height + verticalSpace);
}
_scrollViewer.SetCurrentValue(MaxHeightProperty, maxHeight);
+
+
+ var minHeight = 0.0;
+
+ if (MinLines > 0 && double.IsNaN(Height))
+ {
+ var fontSize = FontSize;
+ var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
+ var paragraphProperties = TextLayout.CreateTextParagraphProperties(typeface, fontSize, null, default, default, null, default, LineHeight, default);
+ var textLayout = new TextLayout(new LineTextSource(MinLines), paragraphProperties);
+ var verticalSpace = GetVerticalSpaceBetweenScrollViewerAndPresenter();
+
+ minHeight = Math.Ceiling(textLayout.Height + verticalSpace);
+ }
+
+ _scrollViewer.SetCurrentValue(MinHeightProperty, minHeight);
}
return base.MeasureOverride(availableSize);
}
- private class MaxLinesTextSource : ITextSource
+ private class LineTextSource : ITextSource
{
- private readonly int _maxLines;
+ private readonly int _lines;
- public MaxLinesTextSource(int maxLines)
+ public LineTextSource(int lines)
{
- _maxLines = maxLines;
+ _lines = lines;
}
public TextRun? GetTextRun(int textSourceIndex)
{
- if (textSourceIndex >= _maxLines)
+ if (textSourceIndex >= _lines)
{
return null;
}
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index 44c997f555..988a265655 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -478,16 +478,11 @@ namespace Avalonia.Controls
child.CloseInternal();
}
- if (Owner is Window owner)
- {
- owner.RemoveChild(this);
- }
-
- Owner = null;
-
PlatformImpl?.Dispose();
_showingAsDialog = false;
+
+ Owner = null;
}
private bool ShouldCancelClose(WindowClosingEventArgs args)
@@ -554,11 +549,6 @@ namespace Avalonia.Controls
StopRendering();
- if (Owner is Window owner)
- {
- owner.RemoveChild(this);
- }
-
if (_children.Count > 0)
{
foreach (var child in _children.ToArray())
@@ -567,10 +557,11 @@ namespace Avalonia.Controls
}
}
- Owner = null;
PlatformImpl?.Hide();
IsVisible = false;
_shown = false;
+
+ Owner = null;
}
}
@@ -689,13 +680,7 @@ namespace Avalonia.Controls
LayoutManager.ExecuteInitialLayoutPass();
- if (PlatformImpl != null && owner?.PlatformImpl is not null)
- {
- PlatformImpl.SetParent(owner.PlatformImpl);
- }
-
Owner = owner;
- owner?.AddChild(this, false);
SetWindowStartupLocation(owner);
@@ -770,9 +755,7 @@ namespace Avalonia.Controls
var result = new TaskCompletionSource();
- PlatformImpl?.SetParent(owner.PlatformImpl!);
Owner = owner;
- owner.AddChild(this, true);
SetWindowStartupLocation(owner);
@@ -974,11 +957,6 @@ namespace Avalonia.Controls
base.HandleClosed();
- if (Owner is Window owner)
- {
- owner.RemoveChild(this);
- }
-
Owner = null;
}
@@ -1031,6 +1009,20 @@ namespace Avalonia.Controls
PlatformImpl?.SetSystemDecorations(typedNewValue);
}
+
+ if (change.Property == OwnerProperty)
+ {
+ var oldParent = change.OldValue as Window;
+ var newParent = change.NewValue as Window;
+
+ oldParent?.RemoveChild(this);
+ newParent?.AddChild(this, _showingAsDialog);
+
+ if (PlatformImpl is IWindowImpl impl)
+ {
+ impl.SetParent(_showingAsDialog ? newParent?.PlatformImpl! : (newParent?.PlatformImpl ?? null));
+ }
+ }
}
protected override AutomationPeer OnCreateAutomationPeer()
diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs
index 1b8e0c18e8..6395529278 100644
--- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs
+++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs
@@ -35,7 +35,7 @@ namespace Avalonia.DesignerSupport
new Uri($"avares://{Path.GetFileNameWithoutExtension(assemblyPath)}{xamlFileProjectPath}");
}
- var localAsm = assemblyPath != null ? Assembly.LoadFile(Path.GetFullPath(assemblyPath)) : null;
+ var localAsm = assemblyPath != null ? Assembly.LoadFrom(Path.GetFullPath(assemblyPath)) : null;
var useCompiledBindings = localAsm?.GetCustomAttributes()
.FirstOrDefault(a => a.Key == "AvaloniaUseCompiledBindingsByDefault")?.Value;
diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
index 24074f9c8c..0dfab530ea 100644
--- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
+++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
@@ -172,7 +172,7 @@ namespace Avalonia.DesignerSupport.Remote
var transport = CreateTransport(args);
if (transport is ITransportWithEnforcedMethod enforcedMethod)
args.Method = enforcedMethod.PreviewerMethod;
- var asm = Assembly.LoadFile(System.IO.Path.GetFullPath(args.AppPath));
+ var asm = Assembly.LoadFrom(System.IO.Path.GetFullPath(args.AppPath));
var entryPoint = asm.EntryPoint ?? throw Die($"Assembly {args.AppPath} doesn't have an entry point");
Log($"Initializing application in design mode");
Design.IsDesignMode = true;
diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
index a77be77c87..4659b79821 100644
--- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
+++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
@@ -13,7 +13,13 @@
-
+
+
diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs
index 1b22393337..7e43aaf943 100644
--- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs
+++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs
@@ -20,7 +20,7 @@ namespace Avalonia.FreeDesktop
return null;
var dbusFileChooser = new OrgFreedesktopPortalFileChooser(DBusHelper.Connection, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop");
- uint version = 0;
+ uint version;
try
{
version = await dbusFileChooser.GetVersionPropertyAsync();
@@ -50,7 +50,7 @@ namespace Avalonia.FreeDesktop
public override bool CanSave => true;
- public override bool CanPickFolder => true;
+ public override bool CanPickFolder => _version >= 3;
public override async Task> OpenFilePickerAsync(FilePickerOpenOptions options)
{
@@ -61,7 +61,7 @@ namespace Avalonia.FreeDesktop
if (filters is not null)
chooserOptions.Add("filters", filters);
- if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath && _version >= 4)
+ if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath)
chooserOptions.Add("current_folder", new DBusVariantItem("ay", new DBusByteArrayItem(Encoding.UTF8.GetBytes(folderPath + "\0"))));
chooserOptions.Add("multiple", new DBusVariantItem("b", new DBusBoolItem(options.AllowMultiple)));
@@ -119,15 +119,21 @@ namespace Avalonia.FreeDesktop
public override async Task> OpenFolderPickerAsync(FolderPickerOpenOptions options)
{
+ if (_version < 3)
+ return Array.Empty();
+
var parentWindow = $"x11:{_handle.Handle:X}";
var chooserOptions = new Dictionary
{
{ "directory", new DBusVariantItem("b", new DBusBoolItem(true)) },
{ "multiple", new DBusVariantItem("b", new DBusBoolItem(options.AllowMultiple)) }
};
- if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath && _version >= 4)
+
+ if (options.SuggestedFileName is { } currentName)
+ chooserOptions.Add("current_name", new DBusVariantItem("s", new DBusStringItem(currentName)));
+ if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath)
chooserOptions.Add("current_folder", new DBusVariantItem("ay", new DBusByteArrayItem(Encoding.UTF8.GetBytes(folderPath + "\0"))));
-
+
var objectPath = await _fileChooser.OpenFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions);
var request = new OrgFreedesktopPortalRequest(_connection, "org.freedesktop.portal.Desktop", objectPath);
var tsc = new TaskCompletionSource();
diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs
index 9106644dc0..76bf2d3bfa 100644
--- a/src/Avalonia.Native/SystemDialogs.cs
+++ b/src/Avalonia.Native/SystemDialogs.cs
@@ -41,7 +41,7 @@ namespace Avalonia.Native
options.AllowMultiple.AsComBool(),
options.Title ?? string.Empty,
suggestedDirectory,
- string.Empty,
+ options.SuggestedFileName ?? string.Empty,
fileTypes);
var result = await events.Task.ConfigureAwait(false);
diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts
index 47c501cbb7..800a93a220 100644
--- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts
+++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts
@@ -206,6 +206,7 @@ export class SizeWatcher {
static observer: ResizeObserver;
static elements: Map;
private static lastMove: number;
+ private static timeoutHandle?: number;
public static observe(element: HTMLElement, elementId: string | undefined, callback: (width: number, height: number) => void): void {
if (!element || !callback) {
@@ -220,6 +221,20 @@ export class SizeWatcher {
if (Date.now() - SizeWatcher.lastMove > 33) {
callback(element.clientWidth, element.clientHeight);
SizeWatcher.lastMove = Date.now();
+ if (SizeWatcher.timeoutHandle) {
+ clearTimeout(SizeWatcher.timeoutHandle);
+ SizeWatcher.timeoutHandle = undefined;
+ }
+ } else {
+ if (SizeWatcher.timeoutHandle) {
+ clearTimeout(SizeWatcher.timeoutHandle);
+ }
+
+ SizeWatcher.timeoutHandle = setTimeout(() => {
+ callback(element.clientWidth, element.clientHeight);
+ SizeWatcher.lastMove = Date.now();
+ SizeWatcher.timeoutHandle = undefined;
+ }, 100);
}
};
diff --git a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index 5d603143de..1d4bae7e6a 100644
--- a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Media;
using Avalonia.Platform;
@@ -48,8 +49,14 @@ namespace Avalonia.Headless
}
public IStreamGeometryImpl CreateStreamGeometry() => new HeadlessStreamingGeometryStub();
- public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => throw new NotImplementedException();
- public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) => throw new NotImplementedException();
+
+ public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) =>
+ new HeadlessGeometryStub(children.Count != 0 ?
+ children.Select(c => c.Bounds).Aggregate((a, b) => a.Union(b)) :
+ default);
+
+ public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2)
+ => new HeadlessGeometryStub(g1.Bounds.Union(g2.Bounds));
public IRenderTarget CreateRenderTarget(IEnumerable