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/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/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/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 427f5c8a9b..345e7fcac8 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -523,25 +523,47 @@ namespace Avalonia.Controls
IInputElement? from,
bool wrap)
{
- IInputElement? result;
- var c = from;
+ var current = from;
- do
+ for (;;)
{
- result = container.GetControl(direction, c, wrap);
+ var result = container.GetControl(direction, current, wrap);
- if (result != null &&
- result.Focusable &&
+ if (result is null)
+ {
+ return null;
+ }
+
+ if (result.Focusable &&
result.IsEffectivelyEnabled &&
result.IsEffectivelyVisible)
{
return result;
}
- c = result;
- } while (c != null && c != from && direction != NavigationDirection.First && direction != NavigationDirection.Last);
+ current = result;
- return null;
+ if (current == from)
+ {
+ return null;
+ }
+
+ switch (direction)
+ {
+ //We did not find an enabled first item. Move downwards until we find one.
+ case NavigationDirection.First:
+ direction = NavigationDirection.Down;
+ from = result;
+ break;
+
+ //We did not find an enabled last item. Move upwards until we find one.
+ case NavigationDirection.Last:
+ direction = NavigationDirection.Up;
+ from = result;
+ break;
+
+ }
+ }
}
private void PresenterChildIndexChanged(object? sender, ChildIndexChangedEventArgs e)
diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
index 868cce879a..16aeb2f559 100644
--- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
+++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
@@ -53,7 +53,7 @@ namespace Avalonia.Controls.Platform
Menu.PointerPressed += PointerPressed;
Menu.PointerReleased += PointerReleased;
Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
- Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, MenuOpened);
+ Menu.AddHandler(MenuBase.MenuOpenedEvent, MenuOpened);
Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved);
@@ -89,7 +89,7 @@ namespace Avalonia.Controls.Platform
Menu.PointerPressed -= PointerPressed;
Menu.PointerReleased -= PointerReleased;
Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
- Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, MenuOpened);
+ Menu.RemoveHandler(MenuBase.MenuOpenedEvent, MenuOpened);
Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved);
@@ -175,7 +175,11 @@ namespace Avalonia.Controls.Platform
case Key.Left:
{
- if (item?.Parent is IMenuItem parent && !parent.IsTopLevel && parent.IsSubMenuOpen)
+ if (item is { IsSubMenuOpen: true, SelectedItem: null })
+ {
+ item.Close();
+ }
+ else if (item?.Parent is IMenuItem { IsTopLevel: false, IsSubMenuOpen: true } parent)
{
parent.Close();
parent.Focus();
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.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json
index 403bb5a59a..2fbcbfdb6a 100644
--- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json
+++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json
@@ -10,6 +10,55 @@
"integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==",
"dev": true
},
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+ "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
+ "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"@types/eslint": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz",
@@ -2136,6 +2185,12 @@
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==",
"dev": true
},
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@@ -2153,6 +2208,16 @@
"source-map-js": "^1.0.1"
}
},
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
"string-template": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz",
@@ -2208,13 +2273,14 @@
"dev": true
},
"terser": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
- "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
+ "version": "5.14.2",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
+ "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
"dev": true,
"requires": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
"commander": "^2.20.0",
- "source-map": "~0.7.2",
"source-map-support": "~0.5.20"
},
"dependencies": {
@@ -2223,30 +2289,6 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
- },
- "source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
- "dev": true
- },
- "source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- }
- }
}
}
},
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 @@