diff --git a/src/Modern.WindowKit/Avalonia.Mac/WindowImplBase.cs b/src/Modern.WindowKit/Avalonia.Mac/WindowImplBase.cs
index 91c5b38..efcf668 100644
--- a/src/Modern.WindowKit/Avalonia.Mac/WindowImplBase.cs
+++ b/src/Modern.WindowKit/Avalonia.Mac/WindowImplBase.cs
@@ -220,17 +220,17 @@ void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
_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 @@ public void Activate()
_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 @@ public bool RawTextInputEvent(uint timeStamp, string text)
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 @@ protected virtual bool ChromeHitTest(RawPointerEventArgs e)
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/Modern.WindowKit/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Modern.WindowKit/Avalonia.Skia/SkiaSharpExtensions.cs
index 036078e..d8b22fe 100644
--- a/src/Modern.WindowKit/Avalonia.Skia/SkiaSharpExtensions.cs
+++ b/src/Modern.WindowKit/Avalonia.Skia/SkiaSharpExtensions.cs
@@ -19,7 +19,8 @@ public static SKFilterQuality ToSKFilterQuality(this BitmapInterpolationMode int
return SKFilterQuality.Medium;
case BitmapInterpolationMode.HighQuality:
return SKFilterQuality.High;
- case BitmapInterpolationMode.Default:
+ case BitmapInterpolationMode.None:
+ case BitmapInterpolationMode.Unspecified:
return SKFilterQuality.None;
default:
throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null);
@@ -30,6 +31,7 @@ public static SKFilterQuality ToSKFilterQuality(this BitmapInterpolationMode int
//{
// switch (blendingMode)
// {
+ // case BitmapBlendingMode.Unspecified:
// case BitmapBlendingMode.SourceOver:
// return SKBlendMode.SrcOver;
// case BitmapBlendingMode.Source:
diff --git a/src/Modern.WindowKit/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Modern.WindowKit/Avalonia.Skia/WriteableBitmapImpl.cs
index 58a84e1..1d1a34f 100644
--- a/src/Modern.WindowKit/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Modern.WindowKit/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -131,7 +131,7 @@ public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat format, Alpha
//}
///
- public void Dispose()
+ public virtual void Dispose()
{
_bitmap.Dispose();
}
diff --git a/src/Modern.WindowKit/Avalonia.Win32/Win32StorageProvider.cs b/src/Modern.WindowKit/Avalonia.Win32/Win32StorageProvider.cs
index b458999..97251e8 100644
--- a/src/Modern.WindowKit/Avalonia.Win32/Win32StorageProvider.cs
+++ b/src/Modern.WindowKit/Avalonia.Win32/Win32StorageProvider.cs
@@ -99,12 +99,14 @@ private unsafe Task> ShowFilePicker(
}
frm.SetOptions(options);
- if (defaultExtension is not null)
+ if (defaultExtension is null)
{
- fixed (char* pExt = defaultExtension)
- {
- frm.SetDefaultExtension(pExt);
- }
+ defaultExtension = String.Empty;
+ }
+
+ fixed (char* pExt = defaultExtension)
+ {
+ frm.SetDefaultExtension(pExt);
}
suggestedFileName ??= "";
diff --git a/src/Modern.WindowKit/BitmapInterpolationMode.cs b/src/Modern.WindowKit/BitmapInterpolationMode.cs
index edc723b..02351a1 100644
--- a/src/Modern.WindowKit/BitmapInterpolationMode.cs
+++ b/src/Modern.WindowKit/BitmapInterpolationMode.cs
@@ -3,12 +3,14 @@
///
/// Controls the performance and quality of bitmap scaling.
///
- public enum BitmapInterpolationMode
+ public enum BitmapInterpolationMode : byte
{
+ Unspecified,
+
///
- /// Uses the default behavior of the underling render backend.
+ /// Disable interpolation.
///
- Default,
+ None,
///
/// The best performance but worst image quality.
@@ -18,7 +20,7 @@ public enum BitmapInterpolationMode
///
/// Good performance and decent image quality.
///
- MediumQuality,
+ MediumQuality,
///
/// Highest quality but worst performance.
diff --git a/src/Modern.WindowKit/DataFormats.cs b/src/Modern.WindowKit/DataFormats.cs
index cb6eadb..b1c7fd7 100644
--- a/src/Modern.WindowKit/DataFormats.cs
+++ b/src/Modern.WindowKit/DataFormats.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Modern.WindowKit.Input
{
@@ -17,7 +18,7 @@ public static class DataFormats
///
/// 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/Modern.WindowKit/DataObjectExtensions.cs b/src/Modern.WindowKit/DataObjectExtensions.cs
index 0b4953b..1378c9b 100644
--- a/src/Modern.WindowKit/DataObjectExtensions.cs
+++ b/src/Modern.WindowKit/DataObjectExtensions.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using Modern.WindowKit.Platform.Storage;
@@ -25,7 +26,7 @@ public static class DataObjectExtensions
///
/// 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/Modern.WindowKit/Dispatcher.Invoke.cs b/src/Modern.WindowKit/Dispatcher.Invoke.cs
index f9f40c9..031c1c5 100644
--- a/src/Modern.WindowKit/Dispatcher.Invoke.cs
+++ b/src/Modern.WindowKit/Dispatcher.Invoke.cs
@@ -248,11 +248,11 @@ public TResult Invoke(Func callback, DispatcherPriority priori
/// 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 DispatcherOperation InvokeAsync(Action callback, DispatcherPriority prior
/// 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 void Post(Action action, DispatcherPriority priority = default)
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 void Post(Action action, DispatcherPriority priority = default)
///
/// 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 Task InvokeAsync(Func callback, DispatcherPriority priority = defau
///
/// 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/Modern.WindowKit/DispatcherFrame.cs b/src/Modern.WindowKit/DispatcherFrame.cs
index 9edb8b3..471654e 100644
--- a/src/Modern.WindowKit/DispatcherFrame.cs
+++ b/src/Modern.WindowKit/DispatcherFrame.cs
@@ -91,31 +91,44 @@ public bool Continue
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
+ 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)
{
- _cancellationTokenSource = new CancellationTokenSource();
- // Wake up the dispatcher in case it has pending jobs
- Dispatcher.RequestProcessing();
- impl.RunLoop(_cancellationTokenSource.Token);
+ // 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;
}
- finally
- {
- _isRunning = false;
- _cancellationTokenSource?.Cancel();
- _cancellationTokenSource = null;
+
+ 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/Modern.WindowKit/DispatcherOperation.cs b/src/Modern.WindowKit/DispatcherOperation.cs
index 3feed69..478ed66 100644
--- a/src/Modern.WindowKit/DispatcherOperation.cs
+++ b/src/Modern.WindowKit/DispatcherOperation.cs
@@ -331,6 +331,8 @@ public DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, F
private TaskCompletionSource TaskCompletionSource => (TaskCompletionSource)TaskSource!;
+ public new TaskAwaiter GetAwaiter() => GetTask().GetAwaiter();
+
public new Task GetTask() => TaskCompletionSource!.Task;
protected override Task GetTaskCore() => GetTask();
diff --git a/src/Modern.WindowKit/DispatcherPriority.cs b/src/Modern.WindowKit/DispatcherPriority.cs
index 59f13dc..d78026b 100644
--- a/src/Modern.WindowKit/DispatcherPriority.cs
+++ b/src/Modern.WindowKit/DispatcherPriority.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Modern.WindowKit.Threading
{
@@ -100,7 +101,7 @@ private DispatcherPriority(int value)
///
/// 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/Modern.WindowKit/ManagedDispatcherImpl.cs b/src/Modern.WindowKit/ManagedDispatcherImpl.cs
index f268485..5707ce9 100644
--- a/src/Modern.WindowKit/ManagedDispatcherImpl.cs
+++ b/src/Modern.WindowKit/ManagedDispatcherImpl.cs
@@ -58,6 +58,10 @@ public void UpdateTimer(long? dueTimeInMs)
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 void RunLoop(CancellationToken token)
else
_wakeup.WaitOne();
}
+
+ registration.Dispose();
}
}
diff --git a/src/Modern.WindowKit/Modern.WindowKit.csproj b/src/Modern.WindowKit/Modern.WindowKit.csproj
index 01f22cf..2f34360 100644
--- a/src/Modern.WindowKit/Modern.WindowKit.csproj
+++ b/src/Modern.WindowKit/Modern.WindowKit.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/src/Modern.WindowKit/Screen.cs b/src/Modern.WindowKit/Screen.cs
index fcbc60e..e87e715 100644
--- a/src/Modern.WindowKit/Screen.cs
+++ b/src/Modern.WindowKit/Screen.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
namespace Modern.WindowKit.Platform
{
@@ -17,7 +18,7 @@ public class Screen
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 @@ public class Screen
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/Modern.WindowKit/Screens.cs b/src/Modern.WindowKit/Screens.cs
index 143dbf1..623e2ed 100644
--- a/src/Modern.WindowKit/Screens.cs
+++ b/src/Modern.WindowKit/Screens.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using Modern.WindowKit.Platform;
@@ -68,7 +69,7 @@ public Screens(IScreenImpl iScreenImpl)
///
/// 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/Modern.WindowKit/StorageProviderHelpers.cs b/src/Modern.WindowKit/StorageProviderHelpers.cs
index 0dbf8f0..38d65b7 100644
--- a/src/Modern.WindowKit/StorageProviderHelpers.cs
+++ b/src/Modern.WindowKit/StorageProviderHelpers.cs
@@ -50,7 +50,8 @@ public static bool TryFilePathToUri(string path, [NotNullWhen(true)] out Uri? ur
}
}
- public static string NameWithExtension(string path, string? defaultExtension, FilePickerFileType? filter)
+ [return: NotNullIfNotNull(nameof(path))]
+ public static string? NameWithExtension(string? path, string? defaultExtension, FilePickerFileType? filter)
{
var name = Path.GetFileName(path);
if (name != null && !Path.HasExtension(name))