diff --git a/Avalonia.sln b/Avalonia.sln index 071d0457b8..4999719676 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -559,6 +559,7 @@ Global {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {EABE2161-989B-42BF-BD8D-1E34B20C21F1} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} + {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index e52430f50b..54acdd9114 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -21,12 +21,12 @@ True - + True diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs b/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs index 521d3674eb..81a5ba536f 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs @@ -22,8 +22,7 @@ public class EmbedSampleGtk : INativeDemoControl var control = createDefault(); var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName, - "..", - "nodes.mp4")); + "..", "NativeControls", "Gtk", "nodes.mp4")); _mplayer = Process.Start(new ProcessStartInfo("mplayer", $"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"") { diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs b/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs index 456f77a44d..b1fef7c013 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using Avalonia.Controls.Platform; using Avalonia.Platform.Interop; +using Avalonia.X11.Interop; using Avalonia.X11.NativeDialogs; using static Avalonia.X11.NativeDialogs.Gtk; using static Avalonia.X11.NativeDialogs.Glib; @@ -10,8 +11,6 @@ namespace ControlCatalog.NetCore; internal class GtkHelper { - private static Task s_gtkTask; - class FileChooser : INativeControlHostDestroyableControlHandle { private readonly IntPtr _widget; @@ -38,11 +37,7 @@ internal class GtkHelper public static INativeControlHostDestroyableControlHandle CreateGtkFileChooser(IntPtr parentXid) { - if (s_gtkTask == null) - s_gtkTask = StartGtk(); - if (!s_gtkTask.Result) - return null; - return RunOnGlibThread(() => + return GtkInteropHelper.RunOnGlibThread(() => { using (var title = new Utf8Buffer("Embedded")) { diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index d0e1bd885e..fab4622303 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -43,6 +43,7 @@ + diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 282973c26a..a571a0518b 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -24,11 +24,12 @@ public class CompositingRenderer : IRendererWithCompositor DrawingContext _recordingContext; private HashSet _dirty = new(); private HashSet _recalculateChildren = new(); - private readonly CompositionTarget _target; private bool _queuedUpdate; private Action _update; private Action _invalidateScene; + internal CompositionTarget CompositionTarget; + /// /// Asks the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered. /// @@ -40,8 +41,8 @@ public class CompositingRenderer : IRendererWithCompositor _root = root; _compositor = compositor; _recordingContext = new DrawingContext(_recorder); - _target = compositor.CreateCompositionTarget(root.CreateRenderTarget); - _target.Root = ((Visual)root!.VisualRoot!).AttachToCompositor(compositor); + CompositionTarget = compositor.CreateCompositionTarget(root.CreateRenderTarget); + CompositionTarget.Root = ((Visual)root!.VisualRoot!).AttachToCompositor(compositor); _update = Update; _invalidateScene = InvalidateScene; } @@ -49,15 +50,15 @@ public class CompositingRenderer : IRendererWithCompositor /// public bool DrawFps { - get => _target.DrawFps; - set => _target.DrawFps = value; + get => CompositionTarget.DrawFps; + set => CompositionTarget.DrawFps = value; } /// public bool DrawDirtyRects { - get => _target.DrawDirtyRects; - set => _target.DrawDirtyRects = value; + get => CompositionTarget.DrawDirtyRects; + set => CompositionTarget.DrawDirtyRects = value; } /// @@ -81,12 +82,11 @@ public class CompositingRenderer : IRendererWithCompositor /// public IEnumerable HitTest(Point p, IVisual root, Func? filter) { - var res = _target.TryHitTest(p, filter); + var res = CompositionTarget.TryHitTest(p, filter); if(res == null) yield break; - for (var index = res.Count - 1; index >= 0; index--) + foreach(var v in res) { - var v = res[index]; if (v is CompositionDrawListVisual dv) { if (filter == null || filter(dv.Visual)) @@ -234,8 +234,8 @@ public class CompositingRenderer : IRendererWithCompositor SyncChildren(v); _dirty.Clear(); _recalculateChildren.Clear(); - _target.Size = _root.ClientSize; - _target.Scaling = _root.RenderScaling; + CompositionTarget.Size = _root.ClientSize; + CompositionTarget.Scaling = _root.RenderScaling; Compositor.InvokeOnNextCommit(_invalidateScene); } @@ -246,24 +246,24 @@ public class CompositingRenderer : IRendererWithCompositor public void Paint(Rect rect) { Update(); - _target.RequestRedraw(); + CompositionTarget.RequestRedraw(); if(RenderOnlyOnRenderThread && Compositor.Loop.RunsInBackground) Compositor.RequestCommitAsync().Wait(); else - _target.ImmediateUIThreadRender(); + CompositionTarget.ImmediateUIThreadRender(); } - public void Start() => _target.IsEnabled = true; + public void Start() => CompositionTarget.IsEnabled = true; public void Stop() { - _target.IsEnabled = false; + CompositionTarget.IsEnabled = false; } public void Dispose() { Stop(); - _target.Dispose(); + CompositionTarget.Dispose(); // Wait for the composition batch to be applied and rendered to guarantee that // render target is not used anymore and can be safely disposed diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs b/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs index 47cfcd325b..49aea1c3dc 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs @@ -54,13 +54,20 @@ internal class CompositionDrawListVisual : CompositionContainerVisual internal override bool HitTest(Point pt, Func? filter) { - if (DrawList == null) + var custom = Visual as ICustomHitTest; + if (DrawList == null && custom == null) return false; if (filter != null && !filter(Visual)) return false; - if (Visual is ICustomHitTest custom) + if (custom != null) + { + // Simulate the old behavior + // TODO: Change behavior once legacy renderers are removed + pt += new Point(Offset.X, Offset.Y); return custom.HitTest(pt); - foreach (var op in DrawList) + } + + foreach (var op in DrawList!) if (op.Item.HitTest(pt)) return true; return false; diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs index 25bbd4dc88..01b2d0d5d9 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs @@ -62,7 +62,7 @@ namespace Avalonia.Rendering.Composition bool TryGetInvertedTransform(CompositionVisual visual, out Matrix matrix) { - var m = visual.TryGetServerTransform(); + var m = visual.TryGetServerGlobalTransform(); if (m == null) { matrix = default; @@ -73,52 +73,44 @@ namespace Avalonia.Rendering.Composition return m33.TryInvert(out matrix); } - bool TryTransformTo(CompositionVisual visual, ref Point v) + bool TryTransformTo(CompositionVisual visual, Point globalPoint, out Point v) { + v = default; if (TryGetInvertedTransform(visual, out var m)) { - v = v * m; + v = globalPoint * m; return true; } return false; } - bool HitTestCore(CompositionVisual visual, Point point, PooledList result, + void HitTestCore(CompositionVisual visual, Point globalPoint, PooledList result, Func? filter) { - //TODO: Check readback too if (visual.Visible == false) - return false; - if (!TryTransformTo(visual, ref point)) - return false; + return; + if (!TryTransformTo(visual, globalPoint, out var point)) + return; if (visual.ClipToBounds && (point.X < 0 || point.Y < 0 || point.X > visual.Size.X || point.Y > visual.Size.Y)) - return false; - if (visual.Clip?.FillContains(point) == false) - return false; + return; - bool success = false; - // Hit-test the current node - if (visual.HitTest(point, filter)) - { - result.Add(visual); - success = true; - } - - // Inspect children too + if (visual.Clip?.FillContains(point) == false) + return; + + // Inspect children if (visual is CompositionContainerVisual cv) for (var c = cv.Children.Count - 1; c >= 0; c--) { var ch = cv.Children[c]; - var hit = HitTestCore(ch, point, result, filter); - if (hit) - return true; + HitTestCore(ch, globalPoint, result, filter); } - - return success; - + + // Hit-test the current node + if (visual.HitTest(point, filter)) + result.Add(visual); } /// diff --git a/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs b/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs new file mode 100644 index 0000000000..045a4f8cc6 --- /dev/null +++ b/src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs @@ -0,0 +1,6 @@ +namespace Avalonia.Rendering.Composition; + +internal interface ICompositionTargetDebugEvents +{ + void RectInvalidated(Rect rc); +} \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index 0fde86e484..882b66bf70 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -33,7 +33,7 @@ namespace Avalonia.Rendering.Composition.Server private HashSet _attachedVisuals = new(); private Queue _adornerUpdateQueue = new(); - + public ICompositionTargetDebugEvents? DebugEvents { get; set; } public ReadbackIndices Readback { get; } = new(); public int RenderedVisuals { get; set; } @@ -173,6 +173,7 @@ namespace Avalonia.Rendering.Composition.Server if(rect.IsEmpty) return; var snapped = SnapToDevicePixels(rect, Scaling); + DebugEvents?.RectInvalidated(rect); _dirtyRect = _dirtyRect.Union(snapped); _redrawRequested = true; } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 6fdf105e58..c0e487f209 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -119,14 +119,15 @@ namespace Avalonia.Rendering.Composition.Server var oldTransformedContentBounds = TransformedOwnContentBounds; var oldCombinedTransformedClipBounds = _combinedTransformedClipBounds; - - var dirtyOldBounds = false; + if (_parent?.IsDirtyComposition == true) { IsDirtyComposition = true; _isDirtyForUpdate = true; - dirtyOldBounds = true; } + + var invalidateOldBounds = _isDirtyForUpdate; + var invalidateNewBounds = _isDirtyForUpdate; GlobalTransformMatrix = newTransform; @@ -157,30 +158,20 @@ namespace Avalonia.Rendering.Composition.Server EffectiveOpacity = Opacity * (Parent?.EffectiveOpacity ?? 1); - IsVisibleInFrame = Visible && EffectiveOpacity > 0.04 && !_isBackface && + IsVisibleInFrame = _parent?.IsVisibleInFrame != false && Visible && EffectiveOpacity > 0.04 && !_isBackface && !_combinedTransformedClipBounds.IsEmpty; - - if (wasVisible != IsVisibleInFrame) - _isDirtyForUpdate = true; - - // Invalidate previous rect and queue new rect based on visibility - if (positionChanged) - { - if (wasVisible) - dirtyOldBounds = true; - if (IsVisibleInFrame) - _isDirtyForUpdate = true; + if (wasVisible != IsVisibleInFrame || positionChanged) + { + invalidateOldBounds |= wasVisible; + invalidateNewBounds |= IsVisibleInFrame; } - + // Invalidate new bounds - if (IsVisibleInFrame && _isDirtyForUpdate) - { - dirtyOldBounds = true; + if (invalidateNewBounds) AddDirtyRect(TransformedOwnContentBounds.Intersect(_combinedTransformedClipBounds)); - } - if (dirtyOldBounds && wasVisible) + if (invalidateOldBounds) AddDirtyRect(oldTransformedContentBounds.Intersect(oldCombinedTransformedClipBounds)); @@ -190,7 +181,7 @@ namespace Avalonia.Rendering.Composition.Server var i = Root!.Readback; ref var readback = ref GetReadback(i.WriteIndex); readback.Revision = root.Revision; - readback.Matrix = CombinedTransformMatrix; + readback.Matrix = GlobalTransformMatrix; readback.TargetId = Root.Id; readback.Visible = IsVisibleInFrame; } diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs index 65237473fb..6db480a966 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs @@ -72,7 +72,7 @@ internal class BatchStreamWriter : IDisposable var size = Unsafe.SizeOf(); if (_currentDataSegment.Data == IntPtr.Zero || _currentDataSegment.ElementCount + size > _memoryPool.BufferSize) NextDataSegment(); - *(T*)((byte*)_currentDataSegment.Data + _currentDataSegment.ElementCount) = item; + Unsafe.WriteUnaligned((byte*)_currentDataSegment.Data + _currentDataSegment.ElementCount, item); _currentDataSegment.ElementCount += size; } @@ -123,7 +123,7 @@ internal class BatchStreamReader : IDisposable if (_memoryOffset + size > _currentDataSegment.ElementCount) throw new InvalidOperationException("Attempted to read more memory then left in the current segment"); - var rv = *(T*)((byte*)_currentDataSegment.Data + _memoryOffset); + var rv = Unsafe.ReadUnaligned((byte*)_currentDataSegment.Data + _memoryOffset); _memoryOffset += size; if (_memoryOffset == _currentDataSegment.ElementCount) { @@ -181,4 +181,4 @@ internal class BatchStreamReader : IDisposable while (_input.Objects.Count > 0) _objectPool.Return(_input.Objects.Dequeue().Data); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs index 32b4ed3026..b0a89c6f92 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; +using Avalonia.Platform; using Avalonia.Threading; namespace Avalonia.Rendering.Composition.Transport; @@ -17,6 +17,7 @@ internal abstract class BatchStreamPoolBase : IDisposable int _usage; readonly int[] _usageStatistics = new int[10]; int _usageStatisticsSlot; + bool _reclaimImmediately; public int CurrentUsage => _usage; public int CurrentPool => _pool.Count; @@ -27,7 +28,10 @@ internal abstract class BatchStreamPoolBase : IDisposable GC.SuppressFinalize(needsFinalize); var updateRef = new WeakReference>(this); - StartUpdateTimer(startTimer, updateRef); + if (AvaloniaLocator.Current.GetService() == null) + _reclaimImmediately = true; + else + StartUpdateTimer(startTimer, updateRef); } static void StartUpdateTimer(Action>? startTimer, WeakReference> updateRef) @@ -90,7 +94,7 @@ internal abstract class BatchStreamPoolBase : IDisposable lock (_pool) { _usage--; - if (!_disposed) + if (!_disposed && !_reclaimImmediately) { _pool.Push(item); return; diff --git a/src/Avalonia.Base/Rendering/Composition/Visual.cs b/src/Avalonia.Base/Rendering/Composition/Visual.cs index f9e1eae2ab..7ebbb0aa96 100644 --- a/src/Avalonia.Base/Rendering/Composition/Visual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Visual.cs @@ -31,7 +31,7 @@ namespace Avalonia.Rendering.Composition } } - internal Matrix4x4? TryGetServerTransform() + internal Matrix4x4? TryGetServerGlobalTransform() { if (Root == null) return null; diff --git a/src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs b/src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs index ca6020128c..ff222658db 100644 --- a/src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs +++ b/src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs @@ -268,11 +268,11 @@ namespace Avalonia.Collections public static DataGridSortDescription FromComparer(IComparer comparer, ListSortDirection direction = ListSortDirection.Ascending) { - return new DataGridComparerSortDesctiption(comparer, direction); + return new DataGridComparerSortDescription(comparer, direction); } } - public class DataGridComparerSortDesctiption : DataGridSortDescription + public class DataGridComparerSortDescription : DataGridSortDescription { private readonly IComparer _innerComparer; private readonly ListSortDirection _direction; @@ -281,7 +281,7 @@ namespace Avalonia.Collections public IComparer SourceComparer => _innerComparer; public override IComparer Comparer => _comparer; public override ListSortDirection Direction => _direction; - public DataGridComparerSortDesctiption(IComparer comparer, ListSortDirection direction) + public DataGridComparerSortDescription(IComparer comparer, ListSortDirection direction) { _innerComparer = comparer; _direction = direction; @@ -300,7 +300,7 @@ namespace Avalonia.Collections public override DataGridSortDescription SwitchSortDirection() { var newDirection = _direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending; - return new DataGridComparerSortDesctiption(_innerComparer, newDirection); + return new DataGridComparerSortDescription(_innerComparer, newDirection); } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index c415f477d4..e57d6bbde2 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -1091,7 +1091,7 @@ namespace Avalonia.Controls { return OwningGrid.DataConnection.SortDescriptions - .OfType() + .OfType() .FirstOrDefault(s => s.SourceComparer == CustomSortComparer); } diff --git a/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs b/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs index 6a7da87387..278e498a67 100644 --- a/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs +++ b/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs @@ -11,9 +11,14 @@ namespace Avalonia.Controls public MenuFlyoutPresenter() :base(new DefaultMenuInteractionHandler(true)) { + } + public MenuFlyoutPresenter(IMenuInteractionHandler menuInteractionHandler) + : base(menuInteractionHandler) + { } + public override void Close() { // DefaultMenuInteractionHandler calls this diff --git a/src/Avalonia.Controls/MenuBase.cs b/src/Avalonia.Controls/MenuBase.cs index 122d45d033..54cbc46a36 100644 --- a/src/Avalonia.Controls/MenuBase.cs +++ b/src/Avalonia.Controls/MenuBase.cs @@ -40,7 +40,7 @@ namespace Avalonia.Controls /// /// Initializes a new instance of the class. /// - public MenuBase() + protected MenuBase() { InteractionHandler = new DefaultMenuInteractionHandler(false); } @@ -49,7 +49,7 @@ namespace Avalonia.Controls /// Initializes a new instance of the class. /// /// The menu interaction handler. - public MenuBase(IMenuInteractionHandler interactionHandler) + protected MenuBase(IMenuInteractionHandler interactionHandler) { InteractionHandler = interactionHandler ?? throw new ArgumentNullException(nameof(interactionHandler)); } diff --git a/src/Avalonia.Controls/TrayIcon.cs b/src/Avalonia.Controls/TrayIcon.cs index 20c8dff8ea..41a1abd838 100644 --- a/src/Avalonia.Controls/TrayIcon.cs +++ b/src/Avalonia.Controls/TrayIcon.cs @@ -61,6 +61,10 @@ namespace Avalonia.Controls args.NewValue.Value.CollectionChanged += Icons_CollectionChanged; } } + else + { + throw new InvalidOperationException("TrayIcon.Icons must be set on the Application."); + } }); var app = Application.Current ?? throw new InvalidOperationException("Application not yet initialized."); @@ -123,9 +127,9 @@ namespace Avalonia.Controls public static readonly StyledProperty IsVisibleProperty = Visual.IsVisibleProperty.AddOwner(); - public static void SetIcons(AvaloniaObject o, TrayIcons trayIcons) => o.SetValue(IconsProperty, trayIcons); + public static void SetIcons(Application o, TrayIcons trayIcons) => o.SetValue(IconsProperty, trayIcons); - public static TrayIcons GetIcons(AvaloniaObject o) => o.GetValue(IconsProperty); + public static TrayIcons GetIcons(Application o) => o.GetValue(IconsProperty); /// /// Gets or sets the property of a TrayIcon. diff --git a/src/Avalonia.FreeDesktop/DBusHelper.cs b/src/Avalonia.FreeDesktop/DBusHelper.cs index 9f9d75b411..ef99838208 100644 --- a/src/Avalonia.FreeDesktop/DBusHelper.cs +++ b/src/Avalonia.FreeDesktop/DBusHelper.cs @@ -24,8 +24,7 @@ namespace Avalonia.FreeDesktop if (_ctx is not null) _ctx?.Post(d, state); else - lock (_lock) - d(state); + d(state); } } diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs index c17d5b993c..7974069184 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -15,27 +15,26 @@ namespace Avalonia.FreeDesktop { internal class DBusSystemDialog : BclStorageProvider { - private static readonly Lazy s_fileChooser = new(() => + private static readonly Lazy s_fileChooser = new(() => DBusHelper.Connection? + .CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop")); + + internal static async Task TryCreate(IPlatformHandle handle) { - var fileChooser = DBusHelper.Connection?.CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); - if (fileChooser is null) - return null; - try + if (handle.HandleDescriptor == "XID" && s_fileChooser.Value is { } fileChooser) { - _ = fileChooser.GetVersionAsync(); - return fileChooser; - } - catch (Exception e) - { - Logger.TryGet(LogEventLevel.Error, LogArea.X11Platform)?.Log(null, $"Unable to connect to org.freedesktop.portal.Desktop: {e.Message}"); - return null; + try + { + await fileChooser.GetVersionAsync(); + return new DBusSystemDialog(fileChooser, handle); + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Error, LogArea.X11Platform)?.Log(null, $"Unable to connect to org.freedesktop.portal.Desktop: {e.Message}"); + return null; + } } - }); - internal static DBusSystemDialog? TryCreate(IPlatformHandle handle) - { - return handle.HandleDescriptor == "XID" && s_fileChooser.Value is { } fileChooser - ? new DBusSystemDialog(fileChooser, handle) : null; + return null; } private readonly IFileChooser _fileChooser; diff --git a/src/Avalonia.Themes.Default/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Default/Controls/NativeMenuBar.xaml index 81bd8f39c5..3b0019eea1 100644 --- a/src/Avalonia.Themes.Default/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Default/Controls/NativeMenuBar.xaml @@ -14,6 +14,7 @@