diff --git a/build/ExternalConsumers.props b/build/ExternalConsumers.props
new file mode 100644
index 0000000000..d79e951330
--- /dev/null
+++ b/build/ExternalConsumers.props
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm
index 6d1ff7cf12..86bacfb819 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.mm
+++ b/native/Avalonia.Native/src/OSX/AvnView.mm
@@ -275,7 +275,7 @@
delta.Y = [event deltaY];
}
- uint32 timestamp = static_cast([event timestamp] * 1000);
+ uint64_t timestamp = static_cast([event timestamp] * 1000);
auto modifiers = [self getModifiers:[event modifierFlags]];
if(type != Move ||
@@ -444,7 +444,7 @@
auto key = s_KeyMap[[event keyCode]];
- uint32_t timestamp = static_cast([event timestamp] * 1000);
+ uint64_t timestamp = static_cast([event timestamp] * 1000);
auto modifiers = [self getModifiers:[event modifierFlags]];
if(_parent != nullptr)
@@ -657,7 +657,7 @@
[self unmarkText];
- uint32_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000);
+ uint64_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000);
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]);
diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm
index 16e1486acc..ef50cdab84 100644
--- a/native/Avalonia.Native/src/OSX/AvnWindow.mm
+++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm
@@ -460,7 +460,7 @@
auto point = [self translateLocalPoint:avnPoint];
AvnVector delta = { 0, 0 };
- _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
+ _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
}
if(!_isTransitioningToFullScreen)
diff --git a/readme.md b/readme.md
index c8135080fe..6dd556bd0d 100644
--- a/readme.md
+++ b/readme.md
@@ -1,26 +1,43 @@
-[](https://avaloniaui.net/xpf)
+
+
[](https://t.me/Avalonia)
[](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) []( https://aka.ms/dotnet-discord) [](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [](#backers) [](#sponsors) 
[](https://www.nuget.org/packages/Avalonia) [](https://www.nuget.org/packages/Avalonia) 
-# ⚠️ **v11 Update - Pausing community contributions**
-
-for more information see [this](https://github.com/AvaloniaUI/Avalonia/discussions/10599) discussion.
+⚠️ **v11 Update - [Pausing community contributions](https://github.com/AvaloniaUI/Avalonia/discussions/10599)**
## 📖 About
-Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.
+[Avalonia](https://avaloniaui.net) is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of platforms such as Windows, macOS, Linux, iOS, Android and WebAssembly. Avalonia is mature and production ready and is used by companies, including [Schneider Electric](https://avaloniaui.net/showcase#se), [Unity](https://avaloniaui.net/showcase#unity), [JetBrains](https://avaloniaui.net/showcase#rider) and [Github](https://avaloniaui.net/showcase#github).
+
+Considered by many to be the spiritual successor to WPF, Avalonia UI provides a familiar, modern development experience for XAML developers creating cross-platform applications. While Avalonia UI is [similar to WPF](https://docs.avaloniaui.net/misc/wpf), it isn't a 1:1 copy, and you'll find plenty of improvements.
-
+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.
-To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). 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. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
+#### Roadmap
+To see the status of some of our features, please see 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.
+
+#### Awesome Avalonia
+[Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
## 🚀 Getting Started
+See our [Get Started](https://avaloniaui.net/GettingStarted) guide to begin developing apps with Avalonia UI.
+
+### Visual Studio
The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](https://docs.avaloniaui.net/docs/getting-started).
+### JetBrains Rider
+[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
+
+Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
+
+### Avalonia Packages
Avalonia is delivered via NuGet package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/
Use these commands in the Package Manager console to install Avalonia manually:
@@ -30,31 +47,26 @@ Install-Package Avalonia.Desktop
```
## Showcase
+[](https://avaloniaui.net/showcase)
-Examples of UIs built with Avalonia
-
-([Lunacy](https://icons8.com/lunacy))
-
-
-([PlasticSCM](https://www.plasticscm.com/))
-
-([WasabiWallet](https://www.wasabiwallet.io/))
-
-## JetBrains Rider
-
-[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
-
-Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
+See what others have built with Avalonia UI on our [Showcase](https://avaloniaui.net/Showcase). We welcome submissions!
## Bleeding Edge Builds
We also have a [nightly build](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) which tracks the current state of master. Although these packages are less stable than the release on NuGet.org, you'll get all the latest features and bugfixes right away and many of our users actually prefer this feed!
-## Documentation
+## Learning
-Documentation can be found at https://docs.avaloniaui.net. We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
+### Documentation
+Documentation can be found at https://docs.avaloniaui.net.
+
+### Tutorials
+We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
+
+### Samples
+We have a [range of samples](https://github.com/AvaloniaUI/Avalonia.Samples) to help you get started.
## Building and Using
@@ -116,3 +128,8 @@ We have a range of [support plans available](https://avaloniaui.net/support) for
## .NET Foundation
This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
+
+## Avalonia XPF
+Unleash the full potential of your existing WPF apps with our cross-platform UI framework, enabling WPF apps to run on macOS and Linux without requiring expensive and risky rewrites.
+
+[](https://avaloniaui.net/xpf)
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 639c27bf03..eafff3b780 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/Avalonia.Base/Data/BindingPriority.cs b/src/Avalonia.Base/Data/BindingPriority.cs
index 5fd5aae43b..cb7f559e0a 100644
--- a/src/Avalonia.Base/Data/BindingPriority.cs
+++ b/src/Avalonia.Base/Data/BindingPriority.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Data
{
@@ -47,7 +48,7 @@ namespace Avalonia.Data
///
Unset = int.MaxValue,
- [Obsolete("Use Template priority")]
+ [Obsolete("Use Template priority"), EditorBrowsable(EditorBrowsableState.Never)]
TemplatedParent = Template,
}
}
diff --git a/src/Avalonia.Base/Data/InstancedBinding.cs b/src/Avalonia.Base/Data/InstancedBinding.cs
index c09c31632e..f93813c0b2 100644
--- a/src/Avalonia.Base/Data/InstancedBinding.cs
+++ b/src/Avalonia.Base/Data/InstancedBinding.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using Avalonia.Reactive;
using ObservableEx = Avalonia.Reactive.Observable;
@@ -49,7 +50,7 @@ namespace Avalonia.Data
///
public IObservable Source { get; }
- [Obsolete("Use Source property")]
+ [Obsolete("Use Source property"), EditorBrowsable(EditorBrowsableState.Never)]
public IObservable Observable => Source;
///
diff --git a/src/Avalonia.Base/Input/DataFormats.cs b/src/Avalonia.Base/Input/DataFormats.cs
index 35d50e669a..f593ed205f 100644
--- a/src/Avalonia.Base/Input/DataFormats.cs
+++ b/src/Avalonia.Base/Input/DataFormats.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Input
{
@@ -17,7 +18,7 @@ namespace Avalonia.Input
///
/// Dataformat for one or more filenames
///
- [Obsolete("Use DataFormats.Files, this format is supported only on desktop platforms.")]
+ [Obsolete("Use DataFormats.Files, this format is supported only on desktop platforms."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly string FileNames = nameof(FileNames);
}
}
diff --git a/src/Avalonia.Base/Input/DataObjectExtensions.cs b/src/Avalonia.Base/Input/DataObjectExtensions.cs
index 6af531b0d8..d2e525cd68 100644
--- a/src/Avalonia.Base/Input/DataObjectExtensions.cs
+++ b/src/Avalonia.Base/Input/DataObjectExtensions.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using Avalonia.Platform.Storage;
@@ -25,7 +26,7 @@ namespace Avalonia.Input
///
/// Collection of file names. If format isn't available, returns null.
///
- [System.Obsolete("Use GetFiles, this method is supported only on desktop platforms.")]
+ [System.Obsolete("Use GetFiles, this method is supported only on desktop platforms."), EditorBrowsable(EditorBrowsableState.Never)]
public static IEnumerable? GetFileNames(this IDataObject dataObject)
{
return (dataObject.Get(DataFormats.FileNames) as IEnumerable)
diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs
index 56a3b0d7a5..50c2faacc0 100644
--- a/src/Avalonia.Base/Media/Color.cs
+++ b/src/Avalonia.Base/Media/Color.cs
@@ -6,6 +6,7 @@
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System;
+using System.ComponentModel;
using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators;
@@ -465,7 +466,7 @@ namespace Avalonia.Media
}
///
- [Obsolete("Use Color.ToUInt32() instead.")]
+ [Obsolete("Use Color.ToUInt32() instead."), EditorBrowsable(EditorBrowsableState.Never)]
public uint ToUint32()
{
return ToUInt32();
diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs
index 18d6968168..3ab946a1db 100644
--- a/src/Avalonia.Base/Media/DrawingContext.cs
+++ b/src/Avalonia.Base/Media/DrawingContext.cs
@@ -5,6 +5,7 @@ using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading;
using Avalonia.Utilities;
using Avalonia.Media.Imaging;
+using System.ComponentModel;
namespace Avalonia.Media
{
@@ -417,11 +418,11 @@ namespace Avalonia.Media
return new PushedState(this);
}
- [Obsolete("Use PushTransform")]
+ [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushPreTransform(Matrix matrix) => PushTransform(matrix);
- [Obsolete("Use PushTransform")]
+ [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushPostTransform(Matrix matrix) => PushTransform(matrix);
- [Obsolete("Use PushTransform")]
+ [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushTransformContainer() => PushTransform(Matrix.Identity);
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
index 253c7075fa..7d4fac337d 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
@@ -185,7 +185,9 @@ namespace Avalonia.Media.TextFormatting
}
//Stop at the first missing glyph
- if (!currentCodepoint.IsBreakChar && !glyphTypeface.TryGetGlyph(currentCodepoint, out _))
+ if (!currentCodepoint.IsBreakChar &&
+ currentCodepoint.GeneralCategory != GeneralCategory.Control &&
+ !glyphTypeface.TryGetGlyph(currentCodepoint, out _))
{
break;
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index 3264d5e88a..1234067844 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
@@ -83,7 +83,7 @@ namespace Avalonia.Media.TextFormatting
///
public override void Draw(DrawingContext drawingContext, Point lineOrigin)
{
- var (currentX, currentY) = lineOrigin;
+ var (currentX, currentY) = lineOrigin + new Point(Start, 0);
foreach (var textRun in _textRuns)
{
@@ -698,7 +698,7 @@ namespace Avalonia.Media.TextFormatting
i = lastRunIndex;
//Possible overlap at runs of different direction
- if (directionalWidth == 0)
+ if (directionalWidth == 0 && i < _textRuns.Length - 1)
{
//In case a run only contains a linebreak we don't want to skip it.
if (currentRun is ShapedTextRun shaped)
@@ -844,7 +844,7 @@ namespace Avalonia.Media.TextFormatting
i = firstRunIndex;
//Possible overlap at runs of different direction
- if (directionalWidth == 0)
+ if (directionalWidth == 0 && i > 0)
{
//In case a run only contains a linebreak we don't want to skip it.
if (currentRun is ShapedTextRun shaped)
@@ -860,8 +860,8 @@ namespace Avalonia.Media.TextFormatting
}
}
- TextBounds? textBounds = null;
int coveredLength;
+ TextBounds? textBounds;
switch (currentDirection)
{
@@ -942,6 +942,13 @@ namespace Avalonia.Media.TextFormatting
new TextRunBounds(
new Rect(startX, 0, drawableTextRun.Size.Width, Height), currentPosition, currentRun.Length, currentRun));
}
+ else
+ {
+ //Add potential TextEndOfParagraph
+ textRunBounds.Add(
+ new TextRunBounds(
+ new Rect(endX, 0, 0, Height), currentPosition, currentRun.Length, currentRun));
+ }
currentPosition += currentRun.Length;
@@ -1007,6 +1014,13 @@ namespace Avalonia.Media.TextFormatting
endX += drawableTextRun.Size.Width;
}
+ else
+ {
+ //Add potential TextEndOfParagraph
+ textRunBounds.Add(
+ new TextRunBounds(
+ new Rect(endX, 0, 0, Height), currentPosition, currentRun.Length, currentRun));
+ }
currentPosition += currentRun.Length;
@@ -1409,8 +1423,6 @@ namespace Avalonia.Media.TextFormatting
var fontMetrics = _paragraphProperties.DefaultTextRunProperties.CachedGlyphTypeface.Metrics;
var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize;
var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight;
-
- var width = 0d;
var widthIncludingWhitespace = 0d;
var trailingWhitespaceLength = 0;
var newLineLength = 0;
@@ -1422,13 +1434,6 @@ namespace Avalonia.Media.TextFormatting
var lineHeight = _paragraphProperties.LineHeight;
- var lastRunIndex = _textRuns.Length - 1;
-
- if (lastRunIndex > 0 && _textRuns[lastRunIndex] is TextEndOfLine)
- {
- lastRunIndex--;
- }
-
for (var index = 0; index < _textRuns.Length; index++)
{
switch (_textRuns[index])
@@ -1486,7 +1491,7 @@ namespace Avalonia.Media.TextFormatting
}
}
- width = widthIncludingWhitespace;
+ var width = widthIncludingWhitespace;
for (var i = _textRuns.Length - 1; i >= 0; i--)
{
diff --git a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
index 699186868a..bb1663eac0 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
@@ -248,11 +248,11 @@ public partial class Dispatcher
/// An operation representing the queued delegate to be invoked.
///
///
- /// Note that the default priority is DispatcherPriority.Normal.
+ /// Note that the default priority is DispatcherPriority.Default.
///
public DispatcherOperation InvokeAsync(Action callback)
{
- return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
+ return InvokeAsync(callback, default, CancellationToken.None);
}
///
@@ -326,11 +326,11 @@ public partial class Dispatcher
/// An operation representing the queued delegate to be invoked.
///
///
- /// Note that the default priority is DispatcherPriority.Normal.
+ /// Note that the default priority is DispatcherPriority.Default.
///
public DispatcherOperation InvokeAsync(Func callback)
{
- return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None);
+ return InvokeAsync(callback, DispatcherPriority.Default, CancellationToken.None);
}
///
@@ -541,6 +541,18 @@ public partial class Dispatcher
InvokeAsyncImpl(new DispatcherOperation(this, priority, action, true), CancellationToken.None);
}
+ ///
+ /// Executes the specified Func<Task> asynchronously on the
+ /// thread that the Dispatcher was created on
+ ///
+ ///
+ /// A Func<Task> delegate to invoke through the dispatcher.
+ ///
+ ///
+ /// An task that completes after the task returned from callback finishes.
+ ///
+ public Task InvokeAsync(Func callback) => InvokeAsync(callback, DispatcherPriority.Default);
+
///
/// Executes the specified Func<Task> asynchronously on the
/// thread that the Dispatcher was created on
@@ -556,11 +568,29 @@ public partial class Dispatcher
///
/// An task that completes after the task returned from callback finishes
///
- public Task InvokeAsync(Func callback, DispatcherPriority priority = default)
+ public Task InvokeAsync(Func callback, DispatcherPriority priority)
{
_ = callback ?? throw new ArgumentNullException(nameof(callback));
return InvokeAsync(callback, priority).GetTask().Unwrap();
}
+
+ ///
+ /// Executes the specified Func<Task<TResult>> asynchronously on the
+ /// thread that the Dispatcher was created on
+ ///
+ ///
+ /// A Func<Task<TResult>> delegate to invoke through the dispatcher.
+ ///
+ ///
+ /// The priority that determines in what order the specified
+ /// callback is invoked relative to the other pending operations
+ /// in the Dispatcher.
+ ///
+ ///
+ /// An task that completes after the task returned from callback finishes
+ ///
+ public Task InvokeAsync(Func> action) =>
+ InvokeAsync(action, DispatcherPriority.Default);
///
/// Executes the specified Func<Task<TResult>> asynchronously on the
@@ -577,7 +607,7 @@ public partial class Dispatcher
///
/// An task that completes after the task returned from callback finishes
///
- public Task InvokeAsync(Func> action, DispatcherPriority priority = default)
+ public Task InvokeAsync(Func> action, DispatcherPriority priority)
{
_ = action ?? throw new ArgumentNullException(nameof(action));
return InvokeAsync>(action, priority).GetTask().Unwrap();
diff --git a/src/Avalonia.Base/Threading/DispatcherFrame.cs b/src/Avalonia.Base/Threading/DispatcherFrame.cs
index 1f8974dfa3..e826432475 100644
--- a/src/Avalonia.Base/Threading/DispatcherFrame.cs
+++ b/src/Avalonia.Base/Threading/DispatcherFrame.cs
@@ -91,31 +91,44 @@ public class DispatcherFrame
internal void Run(IControlledDispatcherImpl impl)
{
- // Since the actual platform run loop is controlled by a Cancellation token, we are restarting
- // it if frame still needs to run
- while (Continue)
- RunCore(impl);
- }
-
- private void RunCore(IControlledDispatcherImpl impl)
- {
- if (_isRunning)
- throw new InvalidOperationException("This frame is already running");
- _isRunning = true;
- try
- {
- _cancellationTokenSource = new CancellationTokenSource();
- // Wake up the dispatcher in case it has pending jobs
- Dispatcher.RequestProcessing();
- impl.RunLoop(_cancellationTokenSource.Token);
- }
- finally
+ Dispatcher.VerifyAccess();
+
+ // Since the actual platform run loop is controlled by a Cancellation token, we have an
+ // outer loop that restarts the platform one in case Continue was set to true after being set to false
+ while (true)
{
- _isRunning = false;
- _cancellationTokenSource?.Cancel();
- _cancellationTokenSource = null;
+ // Take the instance lock since `Continue` is changed from one too
+ lock (Dispatcher.InstanceLock)
+ {
+ if (!Continue)
+ return;
+
+ if (_isRunning)
+ throw new InvalidOperationException("This frame is already running");
+
+ _cancellationTokenSource = new CancellationTokenSource();
+ _isRunning = true;
+ }
+
+ try
+ {
+ // Wake up the dispatcher in case it has pending jobs
+ Dispatcher.RequestProcessing();
+ impl.RunLoop(_cancellationTokenSource.Token);
+ }
+ finally
+ {
+ lock (Dispatcher.InstanceLock)
+ {
+ _isRunning = false;
+ _cancellationTokenSource?.Cancel();
+ _cancellationTokenSource?.Dispose();
+ _cancellationTokenSource = null;
+ }
+ }
}
}
+
internal void MaybeExitOnDispatcherRequest()
{
diff --git a/src/Avalonia.Base/Threading/DispatcherPriority.cs b/src/Avalonia.Base/Threading/DispatcherPriority.cs
index 3017b45dc7..a43dd8e4a2 100644
--- a/src/Avalonia.Base/Threading/DispatcherPriority.cs
+++ b/src/Avalonia.Base/Threading/DispatcherPriority.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Threading
{
@@ -100,7 +101,7 @@ namespace Avalonia.Threading
///
/// The job will be processed with the same priority as data binding.
///
- [Obsolete("WPF compatibility")] public static readonly DispatcherPriority DataBind = new(Layout);
+ [Obsolete("WPF compatibility"), EditorBrowsable(EditorBrowsableState.Never)] public static readonly DispatcherPriority DataBind = new(Layout);
///
/// The job will be processed with normal priority.
diff --git a/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs b/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs
index 9ace215d03..0079515a63 100644
--- a/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs
+++ b/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs
@@ -1,14 +1,15 @@
using System;
+using System.ComponentModel;
namespace Avalonia.VisualTree
{
- [Obsolete("Internal API, will be removed in future versions, you've been warned")]
+ [Obsolete("Internal API, will be removed in future versions, you've been warned"), EditorBrowsable(EditorBrowsableState.Never)]
public interface IVisualWithRoundRectClip
{
///
/// Gets a value indicating the corner radius of control's clip bounds
///
- [Obsolete("Internal API, will be removed in future versions, you've been warned")]
+ [Obsolete("Internal API, will be removed in future versions, you've been warned"), EditorBrowsable(EditorBrowsableState.Never)]
CornerRadius ClipToBoundsRadius { get; }
}
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index 63e28ea14d..97a8c6fe97 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -64,7 +64,7 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- [Obsolete("Use the Placement property instead.")]
+ [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly StyledProperty PlacementModeProperty = PlacementProperty;
///
@@ -157,7 +157,7 @@ namespace Avalonia.Controls
}
///
- [Obsolete("Use the Placement property instead.")]
+ [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public PlacementMode PlacementMode
{
get => GetValue(PlacementProperty);
diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
index b2c138599e..d27479af18 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Controls.Generators
{
@@ -131,10 +132,10 @@ namespace Avalonia.Controls.Generators
///
public void ClearItemContainer(Control container) => _owner.ClearItemContainer(container);
- [Obsolete("Use ItemsControl.ContainerFromIndex")]
+ [Obsolete("Use ItemsControl.ContainerFromIndex"), EditorBrowsable(EditorBrowsableState.Never)]
public Control? ContainerFromIndex(int index) => _owner.ContainerFromIndex(index);
- [Obsolete("Use ItemsControl.IndexFromContainer")]
+ [Obsolete("Use ItemsControl.IndexFromContainer"), EditorBrowsable(EditorBrowsableState.Never)]
public int IndexFromContainer(Control container) => _owner.IndexFromContainer(container);
}
}
diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
index c1cae862a9..717dadb6ea 100644
--- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
namespace Avalonia.Controls.Generators
{
@@ -20,13 +21,13 @@ namespace Avalonia.Controls.Generators
internal TreeContainerIndex(TreeView owner) => _owner = owner;
- [Obsolete("Use TreeView.GetRealizedTreeContainers")]
+ [Obsolete("Use TreeView.GetRealizedTreeContainers"), EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable Containers => _owner.GetRealizedTreeContainers();
- [Obsolete("Use TreeView.TreeContainerFromItem")]
+ [Obsolete("Use TreeView.TreeContainerFromItem"), EditorBrowsable(EditorBrowsableState.Never)]
public Control? ContainerFromItem(object item) => _owner.TreeContainerFromItem(item);
- [Obsolete("Use TreeView.TreeItemFromContainer")]
+ [Obsolete("Use TreeView.TreeItemFromContainer"), EditorBrowsable(EditorBrowsableState.Never)]
public object? ItemFromContainer(Control container) => _owner.TreeItemFromContainer(container);
}
}
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 1c62de9bed..4a0b3c367e 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
+using System.ComponentModel;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
@@ -626,7 +627,7 @@ namespace Avalonia.Controls
/// TreeView to be able to create a . Can be
/// removed in 12.0.
///
- [Obsolete]
+ [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
private protected virtual ItemContainerGenerator CreateItemContainerGenerator()
{
return new ItemContainerGenerator(this);
diff --git a/src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs b/src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs
index 996fff6775..a593caecaf 100644
--- a/src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs
+++ b/src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Threading.Tasks;
using Avalonia.Metadata;
@@ -7,7 +8,7 @@ namespace Avalonia.Controls.Platform
///
/// Defines a platform-specific system dialog implementation.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
[Unstable]
public interface ISystemDialogImpl
{
diff --git a/src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs b/src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs
index 20bfb440e3..37e6272abd 100644
--- a/src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs
+++ b/src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Platform.Storage;
@@ -10,7 +11,7 @@ namespace Avalonia.Controls.Platform
///
/// Defines a platform-specific system dialog implementation.
///
- [Obsolete]
+ [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
internal class SystemDialogImpl : ISystemDialogImpl
{
public async Task ShowFileDialogAsync(FileDialog dialog, Window parent)
diff --git a/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs b/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs
index 20aa91c83e..fdc098777a 100644
--- a/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs
+++ b/src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs
@@ -58,6 +58,10 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl
public void RunLoop(CancellationToken token)
{
+ CancellationTokenRegistration registration = default;
+ if (token.CanBeCanceled)
+ registration = token.Register(() => _wakeup.Set());
+
while (!token.IsCancellationRequested)
{
bool signaled;
@@ -105,5 +109,7 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl
else
_wakeup.WaitOne();
}
+
+ registration.Dispose();
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/Platform/Screen.cs b/src/Avalonia.Controls/Platform/Screen.cs
index 4898c5f912..fde90dc589 100644
--- a/src/Avalonia.Controls/Platform/Screen.cs
+++ b/src/Avalonia.Controls/Platform/Screen.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Avalonia.Platform
{
@@ -17,7 +18,7 @@ namespace Avalonia.Platform
public double Scaling { get; }
///
- [Obsolete("Use the Scaling property instead.")]
+ [Obsolete("Use the Scaling property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public double PixelDensity => Scaling;
///
@@ -43,7 +44,7 @@ namespace Avalonia.Platform
public bool IsPrimary { get; }
///
- [Obsolete("Use the IsPrimary property instead.")]
+ [Obsolete("Use the IsPrimary property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public bool Primary => IsPrimary;
///
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index eda794f33a..80b7841fc7 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -74,7 +74,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the property.
///
- [Obsolete("Use the Placement property instead.")]
+ [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly StyledProperty PlacementModeProperty = PlacementProperty;
///
@@ -241,7 +241,7 @@ namespace Avalonia.Controls.Primitives
}
///
- [Obsolete("Use the Placement property instead.")]
+ [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public PlacementMode PlacementMode
{
get => GetValue(PlacementProperty);
diff --git a/src/Avalonia.Controls/Primitives/ToggleButton.cs b/src/Avalonia.Controls/Primitives/ToggleButton.cs
index dfaf7bbc45..fa1bc76de4 100644
--- a/src/Avalonia.Controls/Primitives/ToggleButton.cs
+++ b/src/Avalonia.Controls/Primitives/ToggleButton.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Data;
@@ -28,7 +29,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the event.
///
- [Obsolete("Use IsCheckedChangedEvent instead.")]
+ [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent CheckedEvent =
RoutedEvent.Register(
nameof(Checked),
@@ -37,7 +38,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the event.
///
- [Obsolete("Use IsCheckedChangedEvent instead.")]
+ [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent UncheckedEvent =
RoutedEvent.Register(
nameof(Unchecked),
@@ -46,7 +47,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the event.
///
- [Obsolete("Use IsCheckedChangedEvent instead.")]
+ [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent IndeterminateEvent =
RoutedEvent.Register(
nameof(Indeterminate),
@@ -72,7 +73,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is checked.
///
- [Obsolete("Use IsCheckedChanged instead.")]
+ [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler? Checked
{
add => AddHandler(CheckedEvent, value);
@@ -82,7 +83,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is unchecked.
///
- [Obsolete("Use IsCheckedChanged instead.")]
+ [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler? Unchecked
{
add => AddHandler(UncheckedEvent, value);
@@ -92,7 +93,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is neither checked nor unchecked.
///
- [Obsolete("Use IsCheckedChanged instead.")]
+ [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler? Indeterminate
{
add => AddHandler(IndeterminateEvent, value);
@@ -168,7 +169,7 @@ namespace Avalonia.Controls.Primitives
/// Called when becomes true.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
- [Obsolete("Use OnIsCheckedChanged instead.")]
+ [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnChecked(RoutedEventArgs e)
{
RaiseEvent(e);
@@ -178,7 +179,7 @@ namespace Avalonia.Controls.Primitives
/// Called when becomes false.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
- [Obsolete("Use OnIsCheckedChanged instead.")]
+ [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnUnchecked(RoutedEventArgs e)
{
RaiseEvent(e);
@@ -188,7 +189,7 @@ namespace Avalonia.Controls.Primitives
/// Called when becomes null.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
- [Obsolete("Use OnIsCheckedChanged instead.")]
+ [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnIndeterminate(RoutedEventArgs e)
{
RaiseEvent(e);
diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs
index 60d5358c49..c65aaafa4b 100644
--- a/src/Avalonia.Controls/Screens.cs
+++ b/src/Avalonia.Controls/Screens.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using Avalonia.Platform;
@@ -68,7 +69,7 @@ namespace Avalonia.Controls
///
/// The window impl for which to retrieve the Screen.
/// The .
- [Obsolete("Use ScreenFromWindow(WindowBase) overload.")]
+ [Obsolete("Use ScreenFromWindow(WindowBase) overload."), EditorBrowsable(EditorBrowsableState.Never)]
public Screen? ScreenFromWindow(IWindowBaseImpl window)
{
return _iScreenImpl.ScreenFromWindow(window);
diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs
index d2b893df37..8d4dab11d7 100644
--- a/src/Avalonia.Controls/SystemDialog.cs
+++ b/src/Avalonia.Controls/SystemDialog.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Platform;
@@ -11,7 +12,7 @@ namespace Avalonia.Controls
///
/// Base class for system file dialogs.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class FileDialog : FileSystemDialog
{
///
@@ -29,7 +30,7 @@ namespace Avalonia.Controls
///
/// Base class for system file and directory dialogs.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class FileSystemDialog : SystemDialog
{
///
@@ -42,7 +43,7 @@ namespace Avalonia.Controls
///
/// Represents a system dialog that prompts the user to select a location for saving a file.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class SaveFileDialog : FileDialog
{
///
@@ -91,7 +92,7 @@ namespace Avalonia.Controls
///
/// Represents a system dialog that allows the user to select one or more files to open.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class OpenFileDialog : FileDialog
{
///
@@ -132,7 +133,7 @@ namespace Avalonia.Controls
///
/// Represents a system dialog that allows the user to select a directory.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class OpenFolderDialog : FileSystemDialog
{
///
@@ -167,7 +168,7 @@ namespace Avalonia.Controls
///
/// Base class for system dialogs.
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class SystemDialog
{
static SystemDialog()
@@ -188,7 +189,7 @@ namespace Avalonia.Controls
///
/// Represents a filter in an or an .
///
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class FileDialogFilter
{
///
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index 19eaaaa0d9..155d7d5f56 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -7,7 +7,6 @@ using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
-using Avalonia.Utilities;
namespace Avalonia.Controls
{
@@ -565,7 +564,8 @@ namespace Avalonia.Controls
context.FillRectangle(background, new Rect(Bounds.Size));
}
- var padding = Padding;
+ var scale = LayoutHelper.GetLayoutScale(this);
+ var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
var top = padding.Top;
var textHeight = TextLayout.Bounds.Height;
@@ -659,7 +659,6 @@ namespace Avalonia.Controls
protected override Size MeasureOverride(Size availableSize)
{
var scale = LayoutHelper.GetLayoutScale(this);
-
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
_constraint = availableSize.Deflate(padding);
@@ -703,19 +702,24 @@ namespace Avalonia.Controls
}
}
- var measuredSize = TextLayout.Bounds.Size.Inflate(padding);
-
- return measuredSize;
+ return TextLayout.Bounds.Size.Inflate(padding);
}
protected override Size ArrangeOverride(Size finalSize)
{
- if (HasComplexContent)
- {
- var scale = LayoutHelper.GetLayoutScale(this);
+ var scale = LayoutHelper.GetLayoutScale(this);
+ var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
- var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
+ //Fixes: #11019
+ if (finalSize.Width < _constraint.Width)
+ {
+ _textLayout?.Dispose();
+ _textLayout = null;
+ _constraint = finalSize.Deflate(padding);
+ }
+ if (HasComplexContent)
+ {
var currentY = padding.Top;
foreach (var textLine in TextLayout.TextLines)
@@ -730,7 +734,7 @@ namespace Avalonia.Controls
&& controlRun.Control is Control control)
{
control.Arrange(
- new Rect(new Point(currentX, currentY),
+ new Rect(new Point(currentX, currentY),
new Size(control.DesiredSize.Width, textLine.Height)));
}
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index e3a9a05951..5122b4aebd 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
+using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Avalonia.Collections;
@@ -715,7 +716,7 @@ namespace Avalonia.Controls
}
}
- [Obsolete]
+ [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
private protected override ItemContainerGenerator CreateItemContainerGenerator()
{
return new TreeItemContainerGenerator(this);
diff --git a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
index e9a75ab46a..86c0bfc588 100644
--- a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
+++ b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
@@ -1,6 +1,7 @@
#nullable enable
using System;
+using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
@@ -39,11 +40,11 @@ namespace Avalonia.Dialogs
return builder;
}
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public static Task ShowManagedAsync(this OpenFileDialog dialog, Window parent,
ManagedFileDialogOptions? options = null) => ShowManagedAsync(dialog, parent, options);
- [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
+ [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public static async Task ShowManagedAsync(this OpenFileDialog dialog, Window parent,
ManagedFileDialogOptions? options = null) where TWindow : Window, new()
{
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 26c3da9d50..b802b1db71 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -220,17 +220,17 @@ namespace Avalonia.Native
_parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint());
}
- void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+ void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
_parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
}
- int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+ int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, uint key)
{
return _parent.RawKeyEvent(type, timeStamp, modifiers, key).AsComBool();
}
- int IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text)
+ int IAvnWindowBaseEvents.RawTextInputEvent(ulong timeStamp, string text)
{
return _parent.RawTextInputEvent(timeStamp, text).AsComBool();
}
@@ -286,7 +286,7 @@ namespace Avalonia.Native
_native?.Activate();
}
- public bool RawTextInputEvent(uint timeStamp, string text)
+ public bool RawTextInputEvent(ulong timeStamp, string text)
{
if (_inputRoot is null)
return false;
@@ -300,7 +300,7 @@ namespace Avalonia.Native
return args.Handled;
}
- public bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+ public bool RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, uint key)
{
if (_inputRoot is null)
return false;
@@ -319,7 +319,7 @@ namespace Avalonia.Native
return false;
}
- public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+ public void RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
if (_inputRoot is null)
return;
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index 09e9168d8f..a58a00d59d 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/src/Avalonia.Native/avn.idl
@@ -1,6 +1,7 @@
@clr-namespace Avalonia.Native.Interop
@clr-access internal
@clr-map bool int
+@clr-map u_int64_t ulong
@cpp-preamble @@
#pragma once
#include "com.h"
@@ -583,12 +584,12 @@ interface IAvnWindowBaseEvents : IUnknown
void Resized([const] AvnSize& size, AvnPlatformResizeReason reason);
void PositionChanged(AvnPoint position);
void RawMouseEvent(AvnRawMouseEventType type,
- uint timeStamp,
+ u_int64_t timeStamp,
AvnInputModifiers modifiers,
AvnPoint point,
AvnVector delta);
- bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key);
- bool RawTextInputEvent(uint timeStamp, [const] char* text);
+ bool RawKeyEvent(AvnRawKeyEventType type, u_int64_t timeStamp, AvnInputModifiers modifiers, uint key);
+ bool RawTextInputEvent(u_int64_t timeStamp, [const] char* text);
void ScalingChanged(double scaling);
void RunRenderPriorityJobs();
void LostFocus();
diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
index 7e73397743..83bf795b03 100644
--- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
+++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
@@ -8,6 +8,8 @@ using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
using Avalonia.VisualTree;
using Avalonia.Platform;
+using System.ComponentModel;
+
namespace Avalonia.OpenGL.Controls
{
public abstract class OpenGlControlBase : Control
@@ -217,7 +219,7 @@ namespace Avalonia.OpenGL.Controls
return true;
}
- [Obsolete("Use RequestNextFrameRendering()")]
+ [Obsolete("Use RequestNextFrameRendering()"), EditorBrowsable(EditorBrowsableState.Never)]
// ReSharper disable once MemberCanBeProtected.Global
public new void InvalidateVisual() => RequestNextFrameRendering();
diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts
index bbc59aba1c..77166e6f21 100644
--- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts
+++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts
@@ -266,7 +266,7 @@ export class InputHelper {
}
public static setCursor(inputElement: HTMLInputElement, kind: string) {
- if (kind === "pointer") {
+ if (kind === "default") {
inputElement.style.removeProperty("cursor");
} else {
inputElement.style.cursor = kind;
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 55a1014188..51f6cc92e9 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -3,10 +3,5 @@
-
-
- Shared\_ModuleInitializer.cs
- false
-
-
+
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index 2c5f3e2ed1..61f5996a94 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -20,7 +20,6 @@
-
diff --git a/tests/Avalonia.Base.UnitTests/DispatcherTests.cs b/tests/Avalonia.Base.UnitTests/DispatcherTests.cs
index 9ba3f3980d..7b401918ce 100644
--- a/tests/Avalonia.Base.UnitTests/DispatcherTests.cs
+++ b/tests/Avalonia.Base.UnitTests/DispatcherTests.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
+using Avalonia.Controls.Platform;
using Avalonia.Threading;
using Avalonia.Utilities;
using Xunit;
@@ -458,4 +460,46 @@ public class DispatcherTests
}
}
+ [Fact]
+ public void DispatcherInvokeAsyncUnwrapsTasks()
+ {
+ int asyncMethodStage = 0;
+
+ async Task AsyncMethod()
+ {
+ asyncMethodStage = 1;
+ await Task.Delay(200);
+ asyncMethodStage = 2;
+ }
+
+ async Task AsyncMethodWithResult()
+ {
+ await Task.Delay(100);
+ return 1;
+ }
+
+ async Task Test()
+ {
+ await Dispatcher.UIThread.InvokeAsync(AsyncMethod);
+ Assert.Equal(2, asyncMethodStage);
+ Assert.Equal(1, await Dispatcher.UIThread.InvokeAsync(AsyncMethodWithResult));
+ asyncMethodStage = 0;
+
+ await Dispatcher.UIThread.InvokeAsync(AsyncMethod, DispatcherPriority.Default);
+ Assert.Equal(2, asyncMethodStage);
+ Assert.Equal(1, await Dispatcher.UIThread.InvokeAsync(AsyncMethodWithResult, DispatcherPriority.Default));
+
+ Dispatcher.UIThread.ExitAllFrames();
+ }
+
+ using (new DispatcherServices(new ManagedDispatcherImpl(null)))
+ {
+ var t = Test();
+ var cts = new CancellationTokenSource();
+ Task.Delay(3000).ContinueWith(_ => cts.Cancel());
+ Dispatcher.UIThread.MainLoop(cts.Token);
+ Assert.True(t.IsCompletedSuccessfully);
+ t.GetAwaiter().GetResult();
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
index d605ecbfda..1a39dd5223 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
@@ -1021,6 +1021,33 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
+ [Fact]
+ public void Should_GetTextBounds_With_EndOfParagraph()
+ {
+ var text = "abc";
+
+ using (Start())
+ {
+ var defaultProperties = new GenericTextRunProperties(Typeface.Default);
+ var textSource = new SingleBufferTextSource(text, defaultProperties, true);
+
+ var formatter = new TextFormatterImpl();
+
+ var textLine =
+ formatter.FormatLine(textSource, 0, double.PositiveInfinity,
+ new GenericTextParagraphProperties(FlowDirection.LeftToRight, TextAlignment.Left,
+ true, true, defaultProperties, TextWrapping.NoWrap, 0, 0, 0));
+
+ var textBounds = textLine.GetTextBounds(3, 1);
+
+ Assert.Equal(1, textBounds.Count);
+
+ var firstBounds = textBounds.First();
+
+ Assert.True(firstBounds.TextRunBounds.Count > 0);
+ }
+ }
+
private class FixedRunsTextSource : ITextSource
{
private readonly IReadOnlyList _textRuns;