diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml
new file mode 100644
index 0000000000..6a7d53544e
--- /dev/null
+++ b/api/Avalonia.nupkg.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ CP0006
+ P:Avalonia.Rendering.Composition.ICompositionGpuImportedObject.ImportCompleted
+ baseline/netstandard2.0/Avalonia.Base.dll
+ target/netstandard2.0/Avalonia.Base.dll
+
+
\ No newline at end of file
diff --git a/build/SourceLink.props b/build/SourceLink.props
index dd7ecc8d2a..b0f1f2c2cc 100644
--- a/build/SourceLink.props
+++ b/build/SourceLink.props
@@ -1,9 +1,9 @@
true
- false
+ true
+ snupkg
true
- $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
@@ -14,10 +14,6 @@
true
-
- embedded
-
-
diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm
index 9cc9fc9523..88cdf4d9de 100644
--- a/native/Avalonia.Native/src/OSX/app.mm
+++ b/native/Avalonia.Native/src/OSX/app.mm
@@ -2,6 +2,7 @@
#include "AvnString.h"
@interface AvnAppDelegate : NSObject
-(AvnAppDelegate* _Nonnull) initWithEvents: (IAvnApplicationEvents* _Nonnull) events;
+-(void) releaseEvents;
@end
NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular;
@@ -15,6 +16,11 @@ ComPtr _events;
return self;
}
+- (void)releaseEvents
+{
+ _events = nil;
+}
+
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
if([[NSApplication sharedApplication] activationPolicy] != AvnDesiredActivationPolicy)
@@ -105,6 +111,18 @@ extern void InitializeAvnApp(IAvnApplicationEvents* events, bool disableAppDeleg
}
}
+extern void ReleaseAvnAppEvents()
+{
+ NSApplication* app = [AvnApplication sharedApplication];
+ id delegate = [app delegate];
+ if ([delegate isMemberOfClass:[AvnAppDelegate class]])
+ {
+ AvnAppDelegate* avnDelegate = delegate;
+ [avnDelegate releaseEvents];
+ [app setDelegate:nil];
+ }
+}
+
HRESULT AvnApplicationCommands::HideApp()
{
START_COM_CALL;
diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h
index 5cf1b94a2f..672525c64a 100644
--- a/native/Avalonia.Native/src/OSX/common.h
+++ b/native/Avalonia.Native/src/OSX/common.h
@@ -38,6 +38,7 @@ extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem ();
extern void InitializeAvnApp(IAvnApplicationEvents* events, bool disableAppDelegate);
+extern void ReleaseAvnAppEvents();
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;
extern NSPoint ToNSPoint (AvnPoint p);
extern NSRect ToNSRect (AvnRect r);
diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm
index 1c7e2cf25a..3fddb72529 100644
--- a/native/Avalonia.Native/src/OSX/main.mm
+++ b/native/Avalonia.Native/src/OSX/main.mm
@@ -197,6 +197,14 @@ class AvaloniaNative : public ComSingleObject();
var right = new List();
- var suppressionFile = Path.Combine(suppressionFilesFolder, GetPackageId(packagePath) + ".nupkg.xml");
+ var packageId = GetPackageId(packagePath);
+ var suppressionFile = Path.Combine(suppressionFilesFolder, packageId + ".nupkg.xml");
// Don't use Path.Combine with these left and right tool parameters.
// Microsoft.DotNet.ApiCompat.Tool is stupid and treats '/' and '\' as different assemblies in suppression files.
@@ -57,7 +58,7 @@ public static class ApiDiffValidation
e.target == baselineDll.target && e.entry.Name == baselineDll.entry.Name);
if (targetDll.entry is null)
{
- throw new InvalidOperationException($"Some assemblies are missing in the new package: {baselineDll.entry.Name} for {baselineDll.target}");
+ throw new InvalidOperationException($"Some assemblies are missing in the new package {packageId}: {baselineDll.entry.Name} for {baselineDll.target}");
}
var targetDllPath = $"target/{targetDll.target}/{targetDll.entry.Name}";
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index b82c446249..bbfc28aa9f 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -288,7 +288,7 @@ partial class Build : NukeBuild
.Executes(async () =>
{
await Task.WhenAll(
- Directory.GetFiles(Parameters.NugetRoot).Select(nugetPackage => ApiDiffValidation.ValidatePackage(
+ Directory.GetFiles(Parameters.NugetRoot, "*.nupkg").Select(nugetPackage => ApiDiffValidation.ValidatePackage(
ApiCompatTool, nugetPackage, Parameters.ApiValidationBaseline,
Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression)));
});
diff --git a/nukebuild/Numerge b/nukebuild/Numerge
index aef10ae67d..9738c6121f 160000
--- a/nukebuild/Numerge
+++ b/nukebuild/Numerge
@@ -1 +1 @@
-Subproject commit aef10ae67dc55c95f49b52a505a0be33bfa297a5
+Subproject commit 9738c6121fdd143c78d3e25686a7e4e13c00f586
diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj
index 43453833d7..64eccbbb3f 100644
--- a/nukebuild/_build.csproj
+++ b/nukebuild/_build.csproj
@@ -8,8 +8,8 @@
1
net7.0
-
-
+
+
@@ -32,9 +32,9 @@
-
-
-
+
+
+
@@ -43,7 +43,5 @@
dirs.proj
-
-
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index 33f22f4d02..6af35ff0d2 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -144,4 +144,30 @@
+
+
+ Build
+
+
+
+
+ http://127.0.0.1:6001
+ $(OutputPath)/$(AssemblyName).dll
+ MainWindow.axaml
+ $(APreviewExecutable)
+ $([System.IO.Path]::ChangeExtension('$(APreviewExecutable)', '.deps.json'))
+ $([System.IO.Path]::ChangeExtension('$(APreviewExecutable)', '.runtimeconfig.json'))
+ $([System.IO.Path]::GetFullPath('$(APreviewFile)'))
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
index aa99360681..fc5e218a92 100644
--- a/readme.md
+++ b/readme.md
@@ -15,7 +15,7 @@ Considered by many to be the spiritual successor to WPF, Avalonia UI provides a
For those seeking a cross-platform WPF, we have created [Avalonia XPF](https://avaloniaui.net/xpf), enabling WPF applications to run on macOS and Linux with little to no code changes. Avalonia XPF is a commercial product and is licensed per-app, per-platform.
#### Roadmap
-To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239).
+To see the status of some of our features, look at our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239).
#### Breaking Changes
You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been.
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index ddc2b7effb..16eb09de65 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -43,21 +43,15 @@
-
-
-
-
-
-
diff --git a/src/Avalonia.Base/Interactivity/EventRoute.cs b/src/Avalonia.Base/Interactivity/EventRoute.cs
index 37539a7153..d0d82b4884 100644
--- a/src/Avalonia.Base/Interactivity/EventRoute.cs
+++ b/src/Avalonia.Base/Interactivity/EventRoute.cs
@@ -120,11 +120,6 @@ namespace Avalonia.Interactivity
return;
}
- if (e.Source is null)
- {
- throw new ArgumentException("Event source may not be null", nameof(e));
- }
-
Interactive? lastTarget = null;
var start = 0;
var end = _route.Count;
diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs
index 0f70386424..fcb2cec733 100644
--- a/src/Avalonia.Base/Media/GlyphRun.cs
+++ b/src/Avalonia.Base/Media/GlyphRun.cs
@@ -424,13 +424,13 @@ namespace Avalonia.Media
///
public CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit)
{
+ var previousCharacterHit = FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _);
+
if (characterHit.TrailingLength != 0)
{
- return new CharacterHit(characterHit.FirstCharacterIndex);
+ return previousCharacterHit;
}
- var previousCharacterHit = FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _);
-
return new CharacterHit(previousCharacterHit.FirstCharacterIndex);
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index ca31d9a6d0..44f53420de 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Media.TextFormatting
internal static Comparer TextBoundsComparer { get; } =
Comparer.Create((x, y) => x.Rectangle.Left.CompareTo(y.Rectangle.Left));
- private IReadOnlyList? _indexedTextRuns;
+ internal IReadOnlyList? _indexedTextRuns;
private readonly TextRun[] _textRuns;
private readonly double _paragraphWidth;
private readonly TextParagraphProperties _paragraphProperties;
@@ -512,38 +512,45 @@ namespace Avalonia.Media.TextFormatting
///
public override CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit)
{
- if (_textRuns.Length == 0)
+ if (_textRuns.Length == 0 || _indexedTextRuns is null)
{
return new CharacterHit();
}
- if (TryFindNextCharacterHit(characterHit, out var nextCharacterHit))
- {
- return nextCharacterHit;
- }
-
- var lastTextPosition = FirstTextSourceIndex + Length;
+ var currentCharacterrHit = characterHit;
+ var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
- // Can't move, we're after the last character
- var runIndex = GetRunIndexAtCharacterIndex(lastTextPosition, LogicalDirection.Forward, out var currentPosition);
+ var currentRun = GetRunAtCharacterIndex(characterIndex, LogicalDirection.Forward, out var currentPosition);
- var currentRun = _textRuns[runIndex];
+ var nextCharacterHit = characterHit;
switch (currentRun)
{
case ShapedTextRun shapedRun:
{
- nextCharacterHit = shapedRun.GlyphRun.GetNextCaretCharacterHit(characterHit);
+ var offset = Math.Max(0, currentPosition - shapedRun.GlyphRun.Metrics.FirstCluster - characterHit.TrailingLength);
+
+ if (offset > 0)
+ {
+ currentCharacterrHit = new CharacterHit(Math.Max(0, characterHit.FirstCharacterIndex - offset), characterHit.TrailingLength);
+ }
+
+ nextCharacterHit = shapedRun.GlyphRun.GetNextCaretCharacterHit(currentCharacterrHit);
+
+ if (offset > 0)
+ {
+ nextCharacterHit = new CharacterHit(nextCharacterHit.FirstCharacterIndex + offset, nextCharacterHit.TrailingLength);
+ }
break;
}
- default:
+ case TextRun:
{
nextCharacterHit = new CharacterHit(currentPosition + currentRun.Length);
break;
}
}
- if (characterHit.FirstCharacterIndex + characterHit.TrailingLength == nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength)
+ if (characterIndex == nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength)
{
return characterHit;
}
@@ -554,17 +561,75 @@ namespace Avalonia.Media.TextFormatting
///
public override CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit)
{
- if (TryFindPreviousCharacterHit(characterHit, out var previousCharacterHit))
+ if (_textRuns.Length == 0 || _indexedTextRuns is null)
+ {
+ return new CharacterHit();
+ }
+
+ if (characterHit.TrailingLength > 0 && characterHit.FirstCharacterIndex <= FirstTextSourceIndex)
+ {
+ return new CharacterHit(FirstTextSourceIndex);
+ }
+
+ var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
+
+ if (characterIndex <= FirstTextSourceIndex)
{
- return previousCharacterHit;
+ return new CharacterHit(FirstTextSourceIndex);
+ }
+
+ var currentCharacterrHit = characterHit;
+
+ var currentRun = GetRunAtCharacterIndex(characterIndex, LogicalDirection.Backward, out var currentPosition);
+
+ if (currentPosition == characterHit.FirstCharacterIndex)
+ {
+ currentRun = GetRunAtCharacterIndex(characterHit.FirstCharacterIndex, LogicalDirection.Backward, out currentPosition);
+ }
+
+ var previousCharacterHit = characterHit;
+
+ switch (currentRun)
+ {
+ case ShapedTextRun shapedRun:
+ {
+ var offset = Math.Max(0, currentPosition - shapedRun.GlyphRun.Metrics.FirstCluster);
+
+ if (offset > 0)
+ {
+ currentCharacterrHit = new CharacterHit(Math.Max(0, characterHit.FirstCharacterIndex - offset), characterHit.TrailingLength);
+ }
+
+ previousCharacterHit = shapedRun.GlyphRun.GetPreviousCaretCharacterHit(currentCharacterrHit);
+
+ if (offset > 0)
+ {
+ previousCharacterHit = new CharacterHit(previousCharacterHit.FirstCharacterIndex + offset, previousCharacterHit.TrailingLength);
+ }
+ break;
+ }
+ case TextRun:
+ {
+ if (characterHit.TrailingLength > 0)
+ {
+ previousCharacterHit = new CharacterHit(currentPosition, currentRun.Length);
+
+ }
+ else
+ {
+ previousCharacterHit = new CharacterHit(currentPosition + currentRun.Length);
+ }
+
+ break;
+ }
}
- if (characterHit.FirstCharacterIndex <= FirstTextSourceIndex)
+ if (characterIndex == previousCharacterHit.FirstCharacterIndex + previousCharacterHit.TrailingLength)
{
- characterHit = new CharacterHit(FirstTextSourceIndex);
+ return characterHit;
}
- return characterHit; // Can't move, we're before the first character
+ return previousCharacterHit;
}
///
@@ -1009,161 +1074,7 @@ namespace Avalonia.Media.TextFormatting
if (_textLineBreak is null && _textRuns.Length > 1 && _textRuns[_textRuns.Length - 1] is TextEndOfLine textEndOfLine)
{
_textLineBreak = new TextLineBreak(textEndOfLine);
- }
- }
-
- ///
- /// Tries to find the next character hit.
- ///
- /// The current character hit.
- /// The next character hit.
- ///
- private bool TryFindNextCharacterHit(CharacterHit characterHit, out CharacterHit nextCharacterHit)
- {
- nextCharacterHit = characterHit;
-
- var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
- var lastCodepointIndex = FirstTextSourceIndex + Length;
-
- if (codepointIndex >= lastCodepointIndex)
- {
- return false; // Cannot go forward anymore
- }
-
- if (codepointIndex < FirstTextSourceIndex)
- {
- codepointIndex = FirstTextSourceIndex;
- }
-
- var runIndex = GetRunIndexAtCharacterIndex(codepointIndex, LogicalDirection.Forward, out var currentPosition);
-
- while (runIndex < _textRuns.Length)
- {
- var currentRun = _textRuns[runIndex];
-
- switch (currentRun)
- {
- case ShapedTextRun shapedRun:
- {
- var foundCharacterHit = shapedRun.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _);
-
- var isAtEnd = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength == FirstTextSourceIndex + Length;
-
- if (isAtEnd && !shapedRun.GlyphRun.IsLeftToRight)
- {
- nextCharacterHit = foundCharacterHit;
-
- return true;
- }
-
- nextCharacterHit = isAtEnd || characterHit.TrailingLength != 0 ?
- foundCharacterHit :
- new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength);
-
- if (isAtEnd || nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex)
- {
- return true;
- }
-
- break;
- }
- default:
- {
- var textPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
-
- if (textPosition == currentPosition)
- {
- nextCharacterHit = new CharacterHit(currentPosition + currentRun.Length);
-
- return true;
- }
-
- break;
- }
- }
-
- currentPosition += currentRun.Length;
- runIndex++;
- }
-
- return false;
- }
-
- ///
- /// Tries to find the previous character hit.
- ///
- /// The current character hit.
- /// The previous character hit.
- ///
- private bool TryFindPreviousCharacterHit(CharacterHit characterHit, out CharacterHit previousCharacterHit)
- {
- var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
-
- if (characterIndex == FirstTextSourceIndex)
- {
- previousCharacterHit = new CharacterHit(FirstTextSourceIndex);
-
- return true;
- }
-
- previousCharacterHit = characterHit;
-
- if (characterIndex < FirstTextSourceIndex)
- {
- return false; // Cannot go backward anymore.
- }
-
- var runIndex = GetRunIndexAtCharacterIndex(characterIndex, LogicalDirection.Backward, out var currentPosition);
-
- while (runIndex >= 0)
- {
- var currentRun = _textRuns[runIndex];
-
- switch (currentRun)
- {
- case ShapedTextRun shapedRun:
- {
- var foundCharacterHit = shapedRun.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _);
-
- if (foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength < characterIndex)
- {
- previousCharacterHit = foundCharacterHit;
-
- return true;
- }
-
- var previousPosition = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength;
-
- if (foundCharacterHit.TrailingLength > 0 && previousPosition == characterIndex)
- {
- previousCharacterHit = new CharacterHit(foundCharacterHit.FirstCharacterIndex);
- }
-
- if (previousCharacterHit != characterHit)
- {
- return true;
- }
-
- break;
- }
- default:
- {
- if (characterIndex == currentPosition + currentRun.Length)
- {
- previousCharacterHit = new CharacterHit(currentPosition);
-
- return true;
- }
-
- break;
- }
- }
-
- currentPosition -= currentRun.Length;
- runIndex--;
}
-
- return false;
}
///
@@ -1173,15 +1084,23 @@ namespace Avalonia.Media.TextFormatting
/// The logical direction.
/// The text position of the found run index.
/// The text run index.
- private int GetRunIndexAtCharacterIndex(int codepointIndex, LogicalDirection direction, out int textPosition)
+ private TextRun? GetRunAtCharacterIndex(int codepointIndex, LogicalDirection direction, out int textPosition)
{
var runIndex = 0;
textPosition = FirstTextSourceIndex;
+
+ if (_indexedTextRuns is null)
+ {
+ return null;
+ }
+
+ TextRun? currentRun = null;
TextRun? previousRun = null;
- while (runIndex < _textRuns.Length)
+ while (runIndex < _indexedTextRuns.Count)
{
- var currentRun = _textRuns[runIndex];
+ var indexedRun = _indexedTextRuns[runIndex];
+ currentRun = indexedRun.TextRun;
switch (currentRun)
{
@@ -1189,64 +1108,49 @@ namespace Avalonia.Media.TextFormatting
{
var firstCluster = shapedRun.GlyphRun.Metrics.FirstCluster;
- if (firstCluster > codepointIndex)
- {
- break;
- }
-
- if (previousRun is ShapedTextRun previousShaped && !previousShaped.ShapedBuffer.IsLeftToRight)
- {
- if (shapedRun.ShapedBuffer.IsLeftToRight)
- {
- if (firstCluster >= codepointIndex)
- {
- return --runIndex;
- }
- }
- else
- {
- if (codepointIndex > firstCluster + currentRun.Length)
- {
- return --runIndex;
- }
- }
- }
+ firstCluster += Math.Max(0, indexedRun.TextSourceCharacterIndex - firstCluster);
if (direction == LogicalDirection.Forward)
{
- if (codepointIndex >= firstCluster && codepointIndex <= firstCluster + currentRun.Length)
+ if (codepointIndex >= firstCluster && codepointIndex < firstCluster + currentRun.Length)
{
- return runIndex;
+ return currentRun;
}
}
else
{
- if (codepointIndex > firstCluster &&
- codepointIndex <= firstCluster + currentRun.Length)
+ if (previousRun is not null && previousRun is not ShapedTextRun && codepointIndex == textPosition + firstCluster)
+ {
+ textPosition -= previousRun.Length;
+
+ return previousRun;
+ }
+
+ if (codepointIndex > firstCluster && codepointIndex <= firstCluster + currentRun.Length)
{
- return runIndex;
+ return currentRun;
}
}
if (runIndex + 1 >= _textRuns.Length)
{
- return runIndex;
+ return currentRun;
}
textPosition += currentRun.Length;
break;
}
- default:
+ case TextRun:
{
if (codepointIndex == textPosition)
{
- return runIndex;
+ return currentRun;
}
if (runIndex + 1 >= _textRuns.Length)
{
- return runIndex;
+ return currentRun;
}
textPosition += currentRun.Length;
@@ -1257,10 +1161,11 @@ namespace Avalonia.Media.TextFormatting
}
runIndex++;
+
previousRun = currentRun;
}
- return runIndex;
+ return currentRun;
}
private TextLineMetrics CreateLineMetrics()
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs
index ce728f86a2..31cbaf2a29 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositionExternalMemory.cs
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Threading.Tasks;
using Avalonia.Metadata;
using Avalonia.Platform;
namespace Avalonia.Rendering.Composition;
+
+[NotClientImplementable]
public interface ICompositionGpuInterop
{
///
@@ -84,14 +87,22 @@ public enum CompositionGpuImportedImageSynchronizationCapabilities
///
/// An imported GPU object that's usable by composition APIs
///
+[NotClientImplementable]
public interface ICompositionGpuImportedObject : IAsyncDisposable
{
///
/// Tracks the import status of the object. Once the task is completed,
/// the user code is allowed to free the resource owner in case when a non-owning
- /// sharing handle was used
+ /// sharing handle was used.
///
+ Task ImportCompleted { get; }
+
+ ///
+ /// ImportCompleted (recommended replacement)
+ [Obsolete("Please use ICompositionGpuImportedObject.ImportCompleted instead")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
Task ImportCompeted { get; }
+
///
/// Indicates if the device context this instance is associated with is no longer available
///
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs
index 1643ec6e8d..12a252ed96 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositionInterop.cs
@@ -67,18 +67,20 @@ abstract class CompositionGpuImportedObjectBase : ICompositionGpuImportedObject
Context = context;
Feature = feature;
- ImportCompeted = Compositor.InvokeServerJobAsync(Import);
+ ImportCompleted = Compositor.InvokeServerJobAsync(Import);
}
protected abstract void Import();
public abstract void Dispose();
- public Task ImportCompeted { get; }
+ public Task ImportCompleted { get; }
+
+ public Task ImportCompeted => ImportCompleted;
public bool IsLost => Context.IsLost;
public ValueTask DisposeAsync() => new(Compositor.InvokeServerJobAsync(() =>
{
- if (ImportCompeted.Status == TaskStatus.RanToCompletion)
+ if (ImportCompleted.Status == TaskStatus.RanToCompletion)
Dispose();
}));
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs
index cb5e21be00..97083ccaf5 100644
--- a/src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionRenderData.cs
@@ -42,22 +42,31 @@ class ServerCompositionRenderData : SimpleServerRenderResource
_items.Add(reader.ReadObject());
var collector = s_resourceHashSetPool.Get();
- foreach(var item in _items)
- if (item is IRenderDataItemWithServerResources resourceItem)
- resourceItem.Collect(collector);
-
+ CollectResources(_items, collector);
+
foreach (var r in collector.Resources)
{
_referencedResources.Add(r);
r.AddObserver(this);
}
-
+
collector.Resources.Clear();
s_resourceHashSetPool.ReturnAndSetNull(ref collector);
base.DeserializeChangesCore(reader, committedAt);
}
+ private static void CollectResources(PooledInlineList items, IRenderDataServerResourcesCollector collector)
+ {
+ foreach (var item in items)
+ {
+ if (item is IRenderDataItemWithServerResources resourceItem)
+ resourceItem.Collect(collector);
+ else if (item is RenderDataPushNode pushNode)
+ CollectResources(pushNode.Children, collector);
+ }
+ }
+
public Rect? Bounds
{
get
@@ -133,4 +142,4 @@ class ServerCompositionRenderData : SimpleServerRenderResource
Reset();
base.Dispose();
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs
index da24b3812a..f58403f0bc 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs
@@ -31,12 +31,12 @@ internal class ServerCompositionDrawingSurface : ServerCompositionSurface, IDisp
throw new PlatformGraphicsContextLostException();
// This should never happen, but check for it anyway to avoid a deadlock
- if (!image.ImportCompeted.IsCompleted)
+ if (!image.ImportCompleted.IsCompleted)
throw new InvalidOperationException("The import operation is not completed yet");
// Rethrow the import here exception
- if (image.ImportCompeted.IsFaulted)
- image.ImportCompeted.GetAwaiter().GetResult();
+ if (image.ImportCompleted.IsFaulted)
+ image.ImportCompleted.GetAwaiter().GetResult();
}
void Update(IBitmapImpl newImage, IPlatformRenderInterfaceContext context)
diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
index e0563876bf..1efaa01442 100644
--- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
+++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
@@ -99,14 +99,15 @@ namespace Avalonia.Threading
}
}
- public static RestoreContext Ensure(DispatcherPriority priority)
+ public static RestoreContext Ensure(DispatcherPriority priority) => Ensure(Dispatcher.UIThread, priority);
+ public static RestoreContext Ensure(Dispatcher dispatcher, DispatcherPriority priority)
{
if (Current is AvaloniaSynchronizationContext avaloniaContext
&& avaloniaContext.Priority == priority)
return default;
var oldContext = Current;
- Dispatcher.UIThread.VerifyAccess();
- SetSynchronizationContext(Dispatcher.UIThread.GetContextWithPriority(priority));
+ dispatcher.VerifyAccess();
+ SetSynchronizationContext(dispatcher.GetContextWithPriority(priority));
return new RestoreContext(oldContext);
}
}
diff --git a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
index 7d586e1cbd..add990bd57 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
@@ -90,7 +90,7 @@ public partial class Dispatcher
{
if (callback == null)
{
- throw new ArgumentNullException("callback");
+ throw new ArgumentNullException(nameof(callback));
}
DispatcherPriority.Validate(priority, "priority");
@@ -98,7 +98,7 @@ public partial class Dispatcher
if (timeout.TotalMilliseconds < 0 &&
timeout != TimeSpan.FromMilliseconds(-1))
{
- throw new ArgumentOutOfRangeException("timeout");
+ throw new ArgumentOutOfRangeException(nameof(timeout));
}
// Fast-Path: if on the same thread, and invoking at Send priority,
@@ -106,7 +106,7 @@ public partial class Dispatcher
// call the callback directly.
if (!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
{
- using (AvaloniaSynchronizationContext.Ensure(priority))
+ using (AvaloniaSynchronizationContext.Ensure(this, priority))
callback();
return;
}
@@ -212,7 +212,7 @@ public partial class Dispatcher
{
if (callback == null)
{
- throw new ArgumentNullException("callback");
+ throw new ArgumentNullException(nameof(callback));
}
DispatcherPriority.Validate(priority, "priority");
@@ -220,7 +220,7 @@ public partial class Dispatcher
if (timeout.TotalMilliseconds < 0 &&
timeout != TimeSpan.FromMilliseconds(-1))
{
- throw new ArgumentOutOfRangeException("timeout");
+ throw new ArgumentOutOfRangeException(nameof(timeout));
}
// Fast-Path: if on the same thread, and invoking at Send priority,
@@ -228,7 +228,7 @@ public partial class Dispatcher
// call the callback directly.
if (!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
{
- using (AvaloniaSynchronizationContext.Ensure(priority))
+ using (AvaloniaSynchronizationContext.Ensure(this, priority))
return callback();
}
@@ -304,7 +304,7 @@ public partial class Dispatcher
{
if (callback == null)
{
- throw new ArgumentNullException("callback");
+ throw new ArgumentNullException(nameof(callback));
}
DispatcherPriority.Validate(priority, "priority");
@@ -379,7 +379,7 @@ public partial class Dispatcher
{
if (callback == null)
{
- throw new ArgumentNullException("callback");
+ throw new ArgumentNullException(nameof(callback));
}
DispatcherPriority.Validate(priority, "priority");
diff --git a/src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs b/src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs
index e1833fef2b..4b60ee7479 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.MainLoop.cs
@@ -49,7 +49,7 @@ public partial class Dispatcher
try
{
_frames.Push(frame);
- using (AvaloniaSynchronizationContext.Ensure(DispatcherPriority.Normal))
+ using (AvaloniaSynchronizationContext.Ensure(this, DispatcherPriority.Normal))
frame.Run(_controlledImpl);
}
finally
diff --git a/src/Avalonia.Base/Threading/Dispatcher.Queue.cs b/src/Avalonia.Base/Threading/Dispatcher.Queue.cs
index 829ab4cf87..1644332aea 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.Queue.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.Queue.cs
@@ -9,7 +9,9 @@ public partial class Dispatcher
private readonly DispatcherPriorityQueue _queue = new();
private bool _signaled;
private bool _explicitBackgroundProcessingRequested;
- private const int MaximumTimeProcessingBackgroundJobs = 50;
+ private const int MaximumInputStarvationTimeInFallbackMode = 50;
+ private const int MaximumInputStarvationTimeInExplicitProcessingExplicitMode = 50;
+ private int _maximumInputStarvationTime;
void RequestBackgroundProcessing()
{
@@ -35,8 +37,8 @@ public partial class Dispatcher
lock (InstanceLock)
{
_explicitBackgroundProcessingRequested = false;
- ExecuteJobsCore();
}
+ ExecuteJobsCore(true);
}
///
@@ -130,10 +132,10 @@ public partial class Dispatcher
lock (InstanceLock)
_signaled = false;
- ExecuteJobsCore();
+ ExecuteJobsCore(false);
}
- void ExecuteJobsCore()
+ void ExecuteJobsCore(bool fromExplicitBackgroundProcessingCallback)
{
long? backgroundJobExecutionStartedAt = null;
while (true)
@@ -151,7 +153,6 @@ public partial class Dispatcher
if (job.Priority > DispatcherPriority.Input)
{
ExecuteJob(job);
- backgroundJobExecutionStartedAt = null;
}
// If platform supports pending input query, ask the platform if we can continue running low priority jobs
else if (_pendingInputImpl?.CanQueryPendingInput == true)
@@ -164,6 +165,13 @@ public partial class Dispatcher
return;
}
}
+ // We can't ask if the implementation has pending input, so we should let it to call us back
+ // Once it thinks that input is handled
+ else if (_backgroundProcessingImpl != null && !fromExplicitBackgroundProcessingCallback)
+ {
+ RequestBackgroundProcessing();
+ return;
+ }
// We can't check if there is pending input, but still need to enforce interactivity
// so we stop processing background jobs after some timeout and start a timer to continue later
else
@@ -171,7 +179,7 @@ public partial class Dispatcher
if (backgroundJobExecutionStartedAt == null)
backgroundJobExecutionStartedAt = Now;
- if (Now - backgroundJobExecutionStartedAt.Value > MaximumTimeProcessingBackgroundJobs)
+ if (Now - backgroundJobExecutionStartedAt.Value > _maximumInputStarvationTime)
{
_signaled = true;
RequestBackgroundProcessing();
diff --git a/src/Avalonia.Base/Threading/Dispatcher.Timers.cs b/src/Avalonia.Base/Threading/Dispatcher.Timers.cs
index bb252b7f55..51408daad1 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.Timers.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.Timers.cs
@@ -127,7 +127,7 @@ public partial class Dispatcher
if (needToPromoteTimers)
PromoteTimers();
if (needToProcessQueue)
- ExecuteJobsCore();
+ ExecuteJobsCore(false);
UpdateOSTimer();
}
diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs
index f257072dc8..46c6699b5a 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.cs
@@ -34,6 +34,9 @@ public partial class Dispatcher : IDispatcher
_controlledImpl = _impl as IControlledDispatcherImpl;
_pendingInputImpl = _impl as IDispatcherImplWithPendingInput;
_backgroundProcessingImpl = _impl as IDispatcherImplWithExplicitBackgroundProcessing;
+ _maximumInputStarvationTime = _backgroundProcessingImpl == null ?
+ MaximumInputStarvationTimeInFallbackMode :
+ MaximumInputStarvationTimeInExplicitProcessingExplicitMode;
if (_backgroundProcessingImpl != null)
_backgroundProcessingImpl.ReadyForBackgroundProcessing += OnReadyForExplicitBackgroundProcessing;
}
diff --git a/src/Avalonia.Base/Threading/DispatcherFrame.cs b/src/Avalonia.Base/Threading/DispatcherFrame.cs
index e826432475..1a80740420 100644
--- a/src/Avalonia.Base/Threading/DispatcherFrame.cs
+++ b/src/Avalonia.Base/Threading/DispatcherFrame.cs
@@ -38,10 +38,14 @@ public class DispatcherFrame
/// for their important criteria to be met. These frames
/// should have a timeout associated with them.
///
- public DispatcherFrame(bool exitWhenRequested)
+ public DispatcherFrame(bool exitWhenRequested) : this(Dispatcher.UIThread, exitWhenRequested)
{
- Dispatcher = Dispatcher.UIThread;
Dispatcher.VerifyAccess();
+ }
+
+ internal DispatcherFrame(Dispatcher dispatcher, bool exitWhenRequested)
+ {
+ Dispatcher = dispatcher;
_exitWhenRequested = exitWhenRequested;
_continue = true;
}
diff --git a/src/Avalonia.Base/Threading/DispatcherOperation.cs b/src/Avalonia.Base/Threading/DispatcherOperation.cs
index 8bd6d3bc01..0008d771c6 100644
--- a/src/Avalonia.Base/Threading/DispatcherOperation.cs
+++ b/src/Avalonia.Base/Threading/DispatcherOperation.cs
@@ -258,7 +258,7 @@ public class DispatcherOperation
try
{
- using (AvaloniaSynchronizationContext.Ensure(Priority))
+ using (AvaloniaSynchronizationContext.Ensure(Dispatcher, Priority))
InvokeCore();
}
finally
diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs
index 879d9d8a5f..fbdeed4edc 100644
--- a/src/Avalonia.Base/Threading/DispatcherTimer.cs
+++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs
@@ -62,7 +62,7 @@ public partial class DispatcherTimer
{
if (callback == null)
{
- throw new ArgumentNullException("callback");
+ throw new ArgumentNullException(nameof(callback));
}
Tick += callback;
@@ -112,11 +112,11 @@ public partial class DispatcherTimer
bool updateOSTimer = false;
if (value.TotalMilliseconds < 0)
- throw new ArgumentOutOfRangeException("value",
+ throw new ArgumentOutOfRangeException(nameof(value),
"TimeSpan period must be greater than or equal to zero.");
if (value.TotalMilliseconds > Int32.MaxValue)
- throw new ArgumentOutOfRangeException("value",
+ throw new ArgumentOutOfRangeException(nameof(value),
"TimeSpan period must be less than or equal to Int32.MaxValue.");
lock (_instanceLock)
@@ -253,20 +253,20 @@ public partial class DispatcherTimer
{
if (dispatcher == null)
{
- throw new ArgumentNullException("dispatcher");
+ throw new ArgumentNullException(nameof(dispatcher));
}
DispatcherPriority.Validate(priority, "priority");
if (priority == DispatcherPriority.Inactive)
{
- throw new ArgumentException("Specified priority is not valid.", "priority");
+ throw new ArgumentException("Specified priority is not valid.", nameof(priority));
}
if (interval.TotalMilliseconds < 0)
- throw new ArgumentOutOfRangeException("interval", "TimeSpan period must be greater than or equal to zero.");
+ throw new ArgumentOutOfRangeException(nameof(interval), "TimeSpan period must be greater than or equal to zero.");
if (interval.TotalMilliseconds > Int32.MaxValue)
- throw new ArgumentOutOfRangeException("interval",
+ throw new ArgumentOutOfRangeException(nameof(interval),
"TimeSpan period must be less than or equal to Int32.MaxValue.");
@@ -349,4 +349,4 @@ public partial class DispatcherTimer
// used by Dispatcher
internal long DueTimeInMs { get; private set; }
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Base/Utilities/SmallDictionary.cs b/src/Avalonia.Base/Utilities/SmallDictionary.cs
index b6e62411da..1e63d554b4 100644
--- a/src/Avalonia.Base/Utilities/SmallDictionary.cs
+++ b/src/Avalonia.Base/Utilities/SmallDictionary.cs
@@ -51,7 +51,7 @@ internal struct InlineDictionary : IEnumerable : IEnumerable= Count || index < 0)
{
- throw new ArgumentOutOfRangeException("index");
+ throw new ArgumentOutOfRangeException(nameof(index));
}
if (IsGrouping)
diff --git a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
index 5d080f5ef6..b2deb1c26e 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
@@ -222,7 +222,7 @@ namespace Avalonia.Controls
{
if (element == null)
{
- throw new ArgumentNullException("element");
+ throw new ArgumentNullException(nameof(element));
}
if (element is CheckBox checkBox)
{
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs
index e7f9a9a6c4..798228c84c 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs
@@ -191,7 +191,7 @@ namespace Avalonia.Controls
}
if (dataGridColumn == null)
{
- throw new ArgumentNullException("dataGridColumn");
+ throw new ArgumentNullException(nameof(dataGridColumn));
}
int columnIndexWithFiller = columnIndex;
diff --git a/src/Avalonia.Controls.DataGrid/DataGridLength.cs b/src/Avalonia.Controls.DataGrid/DataGridLength.cs
index 4841ddd494..a193352f52 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridLength.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridLength.cs
@@ -499,7 +499,7 @@ namespace Avalonia.Controls
{
if (destinationType == null)
{
- throw new ArgumentNullException("destinationType");
+ throw new ArgumentNullException(nameof(destinationType));
}
if (destinationType != typeof(string))
{
diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
index 756951667b..c19d887230 100644
--- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
@@ -18,7 +18,7 @@ namespace Avalonia.Automation.Peers
public ControlAutomationPeer(Control owner)
{
- Owner = owner ?? throw new ArgumentNullException("owner");
+ Owner = owner ?? throw new ArgumentNullException(nameof(owner));
Initialize();
}
diff --git a/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs
index 835ed1c4af..674eb9a241 100644
--- a/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs
@@ -146,12 +146,12 @@ namespace Avalonia.Automation.Peers
if (scrollHorizontally && (horizontalPercent < 0.0) || (horizontalPercent > 100.0))
{
- throw new ArgumentOutOfRangeException("horizontalPercent");
+ throw new ArgumentOutOfRangeException(nameof(horizontalPercent));
}
if (scrollVertically && (verticalPercent < 0.0) || (verticalPercent > 100.0))
{
- throw new ArgumentOutOfRangeException("verticalPercent");
+ throw new ArgumentOutOfRangeException(nameof(verticalPercent));
}
var offset = Owner.Offset;
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index bce1a3685f..bd05d4947c 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -270,6 +270,7 @@ namespace Avalonia.Controls
{
if (_popup?.IsInsidePopup(source) == true)
{
+ e.Handled = true;
return;
}
}
@@ -516,5 +517,14 @@ namespace Avalonia.Controls
}
}
}
+
+ ///
+ /// Clears the selection
+ ///
+ public void Clear()
+ {
+ SelectedItem = null;
+ SelectedIndex = -1;
+ }
}
}
diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
index 69b0ffe9a6..5a5c3fdf1d 100644
--- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
@@ -165,7 +165,7 @@ namespace Avalonia.Controls.Primitives
set
{
if (value > MaximumValue || value < MinimumValue)
- throw new ArgumentOutOfRangeException("SelectedValue");
+ throw new ArgumentOutOfRangeException(nameof(value));
var sel = CoerceSelected(value);
_selectedValue = sel;
@@ -195,7 +195,7 @@ namespace Avalonia.Controls.Primitives
set
{
if (value <= 0 || value > _range)
- throw new ArgumentOutOfRangeException("Increment");
+ throw new ArgumentOutOfRangeException(nameof(value));
_increment = value;
UpdateHelperInfo();
var sel = CoerceSelected(SelectedValue);
diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
index b5be12fa54..30f9d8f380 100644
--- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
+++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
@@ -1113,11 +1113,11 @@ namespace Avalonia.Controls
}
if (value < Minimum)
{
- throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be greater than Minimum value of {0}", Minimum));
+ throw new ArgumentOutOfRangeException(nameof(value), $"Value must be greater than Minimum value of {Minimum}");
}
else if (value > Maximum)
{
- throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be less than Maximum value of {0}", Maximum));
+ throw new ArgumentOutOfRangeException(nameof(value), $"Value must be less than Maximum value of {Maximum}");
}
}
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index e22d03273a..66ed1a27e2 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -1395,10 +1395,8 @@ namespace Avalonia.Controls.Primitives
public object Evaluate(object? dataContext)
{
- dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
-
// Only update the DataContext if necessary
- if (!dataContext.Equals(DataContext))
+ if (!Equals(dataContext, DataContext))
DataContext = dataContext;
return GetValue(ValueProperty);
diff --git a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs
index 4c1e0c2565..7ff02711d6 100644
--- a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs
+++ b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs
@@ -98,14 +98,14 @@ namespace Avalonia.Controls.PullToRefresh
if (_scrollViewer.Content == null)
{
- throw new ArgumentException(nameof(adaptee), "Adaptee's content property cannot be null.");
+ throw new ArgumentException("Adaptee's content property cannot be null.", nameof(adaptee));
}
var content = adaptee.Content as Visual;
if (content == null)
{
- throw new ArgumentException(nameof(adaptee), "Adaptee's content property must be a Visual");
+ throw new ArgumentException("Adaptee's content property must be a Visual", nameof(adaptee));
}
if (content.GetVisualParent() == null)
@@ -118,7 +118,7 @@ namespace Avalonia.Controls.PullToRefresh
if (content.Parent is not InputElement)
{
- throw new ArgumentException(nameof(adaptee), "Adaptee's content's parent must be a InputElement");
+ throw new ArgumentException("Adaptee's content's parent must be a InputElement", nameof(adaptee));
}
}
@@ -194,12 +194,12 @@ namespace Avalonia.Controls.PullToRefresh
var content = _scrollViewer?.Content as Visual;
if (content == null)
{
- throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content property must be a Visual");
+ throw new ArgumentException("Adaptee's content property must be a Visual", nameof(_scrollViewer));
}
if (content.Parent is not InputElement parent)
{
- throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content parent must be an InputElement");
+ throw new ArgumentException("Adaptee's content parent must be an InputElement", nameof(_scrollViewer));
}
MakeInteractionSource(parent);
diff --git a/src/Avalonia.Controls/Utils/StringUtils.cs b/src/Avalonia.Controls/Utils/StringUtils.cs
index 2ea47b442a..0ae1ef0b97 100644
--- a/src/Avalonia.Controls/Utils/StringUtils.cs
+++ b/src/Avalonia.Controls/Utils/StringUtils.cs
@@ -76,7 +76,7 @@ namespace Avalonia.Controls.Utils
}
var codepoint = new Codepoint(text[index]);
-
+
if (!codepoint.IsWhiteSpace)
{
return false;
@@ -85,12 +85,20 @@ namespace Avalonia.Controls.Utils
// preceeded by lwsp.
if (index > 0)
{
- var nextCodePoint = new Codepoint(text[index + 1]);
+ if (index + 1 < text.Length)
+ {
+ var nextCodePoint = new Codepoint(text[index + 1]);
- if (nextCodePoint.IsBreakChar)
+ if (nextCodePoint.IsBreakChar)
+ {
+ return true;
+ }
+ }
+ else
{
return true;
}
+
}
switch (codepoint.GeneralCategory)
@@ -125,7 +133,7 @@ namespace Avalonia.Controls.Utils
}
cursor = Math.Min(cursor, text.Length);
-
+
int begin;
int i;
int cr;
@@ -181,7 +189,7 @@ namespace Avalonia.Controls.Utils
{
return cursor;
}
-
+
if (cr < text.Length && text[cr] == '\r' && cr + 1 < text.Length && text[cr + 1] == '\n')
{
lf = cr + 1;
@@ -214,9 +222,9 @@ namespace Avalonia.Controls.Utils
{
return i;
}
-
+
var cc = GetCharClass(text[i]);
-
+
// skip over the word, punctuation, or run of whitespace
while (i < cr && GetCharClass(text[i]) == cc)
{
diff --git a/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs b/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs
index f92756c59b..eaf501b45f 100644
--- a/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs
+++ b/src/Avalonia.Controls/Utils/VirtualizingSnapPointsList.cs
@@ -39,7 +39,7 @@ namespace Avalonia.Controls.Utils
get
{
if(index < 0 || index >= Count)
- throw new ArgumentOutOfRangeException("index");
+ throw new ArgumentOutOfRangeException(nameof(index));
index += _start;
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index 37e5e41faf..b7c58cded9 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -3,9 +3,7 @@ using System.Runtime.InteropServices;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
-using Avalonia.MicroCom;
using Avalonia.Native.Interop;
-using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
@@ -163,6 +161,14 @@ namespace Avalonia.Native
Compositor = new Compositor(_platformGraphics, true);
+
+ AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
+ }
+
+ private void OnProcessExit(object? sender, EventArgs e)
+ {
+ AppDomain.CurrentDomain.ProcessExit -= OnProcessExit;
+ _factory.Dispose();
}
public ITrayIconImpl CreateTrayIcon()
diff --git a/src/Avalonia.Native/NativePlatformSettings.cs b/src/Avalonia.Native/NativePlatformSettings.cs
index 53d3da0378..42d05d831c 100644
--- a/src/Avalonia.Native/NativePlatformSettings.cs
+++ b/src/Avalonia.Native/NativePlatformSettings.cs
@@ -23,7 +23,7 @@ internal class NativePlatformSettings : DefaultPlatformSettings
AvnPlatformThemeVariant.Dark => (PlatformThemeVariant.Dark, ColorContrastPreference.NoPreference),
AvnPlatformThemeVariant.Light => (PlatformThemeVariant.Light, ColorContrastPreference.NoPreference),
AvnPlatformThemeVariant.HighContrastDark => (PlatformThemeVariant.Dark, ColorContrastPreference.High),
- AvnPlatformThemeVariant.HighContrastLight => (PlatformThemeVariant.Dark, ColorContrastPreference.High),
+ AvnPlatformThemeVariant.HighContrastLight => (PlatformThemeVariant.Light, ColorContrastPreference.High),
_ => throw new ArgumentOutOfRangeException()
};
var color = _platformSettings.AccentColor;
diff --git a/src/Avalonia.Remote.Protocol/MetsysBson.cs b/src/Avalonia.Remote.Protocol/MetsysBson.cs
index 8966dd4206..9c5f92b6ac 100644
--- a/src/Avalonia.Remote.Protocol/MetsysBson.cs
+++ b/src/Avalonia.Remote.Protocol/MetsysBson.cs
@@ -752,7 +752,7 @@ namespace Metsys.Bson
if (memberExpression.Expression.NodeType != ExpressionType.Parameter && memberExpression.Expression.NodeType != ExpressionType.Convert)
{
- throw new ArgumentException(string.Format("Expression '{0}' must resolve to top-level member.", lambdaExpression), nameof(lambdaExpression));
+ throw new ArgumentException($"Expression '{lambdaExpression}' must resolve to top-level member.", nameof(lambdaExpression));
}
return memberExpression.Member.Name;
default:
@@ -942,7 +942,7 @@ namespace Metsys.Bson
return new ListWrapper();
}
}
- throw new BsonException(string.Format("Collection of type {0} cannot be deserialized", type.FullName));
+ throw new BsonException($"Collection of type {type.FullName} cannot be deserialized");
}
public abstract void Add(object value);
@@ -1514,7 +1514,7 @@ namespace Metsys.Bson.Configuration
result = Visit((MemberExpression)expression.Left);
}
var index = Expression.Lambda(expression.Right).Compile().DynamicInvoke();
- return result + string.Format("[{0}]", index);
+ return result + $"[{index}]";
}
private string Visit(MemberExpression expression)
@@ -1540,7 +1540,7 @@ namespace Metsys.Bson.Configuration
if (expression.Method.Name == "get_Item" && expression.Arguments.Count == 1)
{
var index = Expression.Lambda(expression.Arguments[0]).Compile().DynamicInvoke();
- name += string.Format("[{0}]", index);
+ name += $"[{index}]";
}
return name;
}
diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs
index 6e18151fae..637d44d617 100644
--- a/src/Avalonia.X11/X11Clipboard.cs
+++ b/src/Avalonia.X11/X11Clipboard.cs
@@ -14,6 +14,7 @@ namespace Avalonia.X11
private readonly X11Info _x11;
private IDataObject _storedDataObject;
private IntPtr _handle;
+ private TaskCompletionSource _storeAtomTcs;
private TaskCompletionSource _requestedFormatsTcs;
private TaskCompletionSource