diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index c5a719ce90..df070c35cf 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1 +1,2 @@
+github: avaloniaui
open_collective: avalonia
diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index 1b60bb4df9..5838519596 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -8,7 +8,7 @@
https://github.com/AvaloniaUI/Avalonia/
true
CS1591
- latest
+ preview
MIT
Icon.png
Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS and with experimental support for Android, iOS and WebAssembly.
diff --git a/native/Avalonia.Native/src/OSX/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm
index b9c75ed742..83ab1bfd01 100644
--- a/native/Avalonia.Native/src/OSX/Screens.mm
+++ b/native/Avalonia.Native/src/OSX/Screens.mm
@@ -41,9 +41,9 @@ public:
ret->WorkingArea.X = [screen visibleFrame].origin.x;
ret->WorkingArea.Y = ConvertPointY(ToAvnPoint([screen visibleFrame].origin)).Y - ret->WorkingArea.Height;
- ret->PixelDensity = [screen backingScaleFactor];
+ ret->Scaling = [screen backingScaleFactor];
- ret->Primary = index == 0;
+ ret->IsPrimary = index == 0;
return S_OK;
}
diff --git a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
index 0ddec3444b..06a5466619 100644
--- a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
+++ b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
@@ -16,9 +16,8 @@
full
true
true
- true
- -O3
- -O3
+ -O2
+ -O2
diff --git a/samples/ControlCatalog.Web/Roots.xml b/samples/ControlCatalog.Web/Roots.xml
index 3c13098159..b07fd86fa2 100644
--- a/samples/ControlCatalog.Web/Roots.xml
+++ b/samples/ControlCatalog.Web/Roots.xml
@@ -3,4 +3,5 @@
+
diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs
index 823f59e030..ff62b834c4 100644
--- a/samples/ControlCatalog/Pages/ScreenPage.cs
+++ b/samples/ControlCatalog/Pages/ScreenPage.cs
@@ -62,10 +62,10 @@ namespace ControlCatalog.Pages
CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20));
- formattedText = CreateFormattedText($"Scaling: {screen.PixelDensity * 100}%");
+ formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40));
- formattedText = CreateFormattedText($"Primary: {screen.Primary}");
+ formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60));
formattedText =
diff --git a/src/Avalonia.Base/Input/DragDrop.cs b/src/Avalonia.Base/Input/DragDrop.cs
index 723d577964..fe75f678e1 100644
--- a/src/Avalonia.Base/Input/DragDrop.cs
+++ b/src/Avalonia.Base/Input/DragDrop.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Input
///
/// Event which is raised, when a drag-and-drop operation leaves the element.
///
- public static readonly RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
+ public static readonly RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
///
/// Event which is raised, when a drag-and-drop operation is updated while over the element.
///
diff --git a/src/Avalonia.Base/Input/DragDropDevice.cs b/src/Avalonia.Base/Input/DragDropDevice.cs
index 30a08eda17..3c91856dcd 100644
--- a/src/Avalonia.Base/Input/DragDropDevice.cs
+++ b/src/Avalonia.Base/Input/DragDropDevice.cs
@@ -54,7 +54,7 @@ namespace Avalonia.Input
try
{
if (_lastTarget != null)
- _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
+ RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragLeaveEvent, effects, data, modifiers);
return RaiseDragEvent(target, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers);
}
finally
@@ -63,13 +63,13 @@ namespace Avalonia.Input
}
}
- private void DragLeave(IInputElement inputRoot)
+ private void DragLeave(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers)
{
if (_lastTarget == null)
return;
try
{
- _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
+ RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragLeaveEvent, effects, data, modifiers);
}
finally
{
@@ -106,7 +106,7 @@ namespace Avalonia.Input
e.Effects = DragOver(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers);
break;
case RawDragEventType.DragLeave:
- DragLeave(e.Root);
+ DragLeave(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers);
break;
case RawDragEventType.Drop:
e.Effects = Drop(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers);
diff --git a/src/Avalonia.Base/Media/PathMarkupParser.cs b/src/Avalonia.Base/Media/PathMarkupParser.cs
index 30c5206125..cf12bf5126 100644
--- a/src/Avalonia.Base/Media/PathMarkupParser.cs
+++ b/src/Avalonia.Base/Media/PathMarkupParser.cs
@@ -188,7 +188,7 @@ namespace Avalonia.Media
_isOpen = true;
}
- private void SetFillRule(ref ReadOnlySpan span)
+ private void SetFillRule(scoped ref ReadOnlySpan span)
{
ThrowIfDisposed();
@@ -452,7 +452,7 @@ namespace Avalonia.Media
return !span.IsEmpty && (span[0] == ',' || span[0] == '-' || span[0] == '.' || char.IsDigit(span[0]));
}
- private static bool ReadArgument(ref ReadOnlySpan remaining, out ReadOnlySpan argument)
+ private static bool ReadArgument(scoped ref ReadOnlySpan remaining, out ReadOnlySpan argument)
{
remaining = SkipWhitespace(remaining);
if (remaining.IsEmpty)
diff --git a/src/Avalonia.Controls/Platform/IScreenImpl.cs b/src/Avalonia.Controls/Platform/IScreenImpl.cs
index fcae3b6493..e2a0b3e3f3 100644
--- a/src/Avalonia.Controls/Platform/IScreenImpl.cs
+++ b/src/Avalonia.Controls/Platform/IScreenImpl.cs
@@ -6,8 +6,14 @@ namespace Avalonia.Platform
[Unstable]
public interface IScreenImpl
{
+ ///
+ /// Gets the total number of screens available on the device.
+ ///
int ScreenCount { get; }
+ ///
+ /// Gets the list of all screens available on the device.
+ ///
IReadOnlyList AllScreens { get; }
Screen? ScreenFromWindow(IWindowBaseImpl window);
diff --git a/src/Avalonia.Controls/Platform/Screen.cs b/src/Avalonia.Controls/Platform/Screen.cs
index 976faed3fd..4898c5f912 100644
--- a/src/Avalonia.Controls/Platform/Screen.cs
+++ b/src/Avalonia.Controls/Platform/Screen.cs
@@ -1,21 +1,64 @@
-namespace Avalonia.Platform
+using System;
+
+namespace Avalonia.Platform
{
+ ///
+ /// Represents a single display screen.
+ ///
public class Screen
{
- public double PixelDensity { get; }
+ ///
+ /// Gets the scaling factor applied to the screen by the operating system.
+ ///
+ ///
+ /// Multiply this value by 100 to get a percentage.
+ /// Both X and Y scaling factors are assumed uniform.
+ ///
+ public double Scaling { get; }
+
+ ///
+ [Obsolete("Use the Scaling property instead.")]
+ public double PixelDensity => Scaling;
+ ///
+ /// Gets the overall pixel-size of the screen.
+ ///
+ ///
+ /// This generally is the raw pixel counts in both the X and Y direction.
+ ///
public PixelRect Bounds { get; }
+ ///
+ /// Gets the actual working-area pixel-size of the screen.
+ ///
+ ///
+ /// This area may be smaller than to account for notches and
+ /// other block-out areas such as taskbars etc.
+ ///
public PixelRect WorkingArea { get; }
- public bool Primary { get; }
-
- public Screen(double pixelDensity, PixelRect bounds, PixelRect workingArea, bool primary)
+ ///
+ /// Gets a value indicating whether the screen is the primary one.
+ ///
+ public bool IsPrimary { get; }
+
+ ///
+ [Obsolete("Use the IsPrimary property instead.")]
+ public bool Primary => IsPrimary;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The scaling factor applied to the screen by the operating system.
+ /// The overall pixel-size of the screen.
+ /// The actual working-area pixel-size of the screen.
+ /// Whether the screen is the primary one.
+ public Screen(double scaling, PixelRect bounds, PixelRect workingArea, bool isPrimary)
{
- this.PixelDensity = pixelDensity;
+ this.Scaling = scaling;
this.Bounds = bounds;
this.WorkingArea = workingArea;
- this.Primary = primary;
+ this.IsPrimary = isPrimary;
}
}
}
diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs
index a554f82f61..dde6b71e6e 100644
--- a/src/Avalonia.Controls/Screens.cs
+++ b/src/Avalonia.Controls/Screens.cs
@@ -8,14 +8,31 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
+ ///
+ /// Represents all screens available on a device.
+ ///
public class Screens
{
private readonly IScreenImpl _iScreenImpl;
+ ///
+ /// Gets the total number of screens available on the device.
+ ///
public int ScreenCount => _iScreenImpl?.ScreenCount ?? 0;
+
+ ///
+ /// Gets the list of all screens available on the device.
+ ///
public IReadOnlyList All => _iScreenImpl?.AllScreens ?? Array.Empty();
- public Screen? Primary => All.FirstOrDefault(x => x.Primary);
+ ///
+ /// Gets the primary screen on the device.
+ ///
+ public Screen? Primary => All.FirstOrDefault(x => x.IsPrimary);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
public Screens(IScreenImpl iScreenImpl)
{
_iScreenImpl = iScreenImpl;
@@ -25,14 +42,14 @@ namespace Avalonia.Controls
{
return _iScreenImpl.ScreenFromRect(bounds);
}
-
+
public Screen? ScreenFromWindow(IWindowBaseImpl window)
{
return _iScreenImpl.ScreenFromWindow(window);
}
public Screen? ScreenFromPoint(PixelPoint point)
- {
+ {
return _iScreenImpl.ScreenFromPoint(point);
}
diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs
index ada081b808..2f96e6911f 100644
--- a/src/Avalonia.Controls/TreeViewItem.cs
+++ b/src/Avalonia.Controls/TreeViewItem.cs
@@ -6,6 +6,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree;
+using Avalonia.Threading;
namespace Avalonia.Controls
{
@@ -45,6 +46,8 @@ namespace Avalonia.Controls
private IControl? _header;
private bool _isExpanded;
private int _level;
+ private bool _templateApplied;
+ private bool _deferredBringIntoViewFlag;
///
/// Initializes static members of the class.
@@ -136,15 +139,24 @@ namespace Avalonia.Controls
protected virtual void OnRequestBringIntoView(RequestBringIntoViewEventArgs e)
{
- if (e.TargetObject == this && _header != null)
+ if (e.TargetObject == this)
{
- var m = _header.TransformToVisual(this);
+ if (!_templateApplied)
+ {
+ _deferredBringIntoViewFlag = true;
+ return;
+ }
- if (m.HasValue)
+ if (_header != null)
{
- var bounds = new Rect(_header.Bounds.Size);
- var rect = bounds.TransformToAABB(m.Value);
- e.TargetRect = rect;
+ var m = _header.TransformToVisual(this);
+
+ if (m.HasValue)
+ {
+ var bounds = new Rect(_header.Bounds.Size);
+ var rect = bounds.TransformToAABB(m.Value);
+ e.TargetRect = rect;
+ }
}
}
}
@@ -187,6 +199,12 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_header = e.NameScope.Find("PART_Header");
+ _templateApplied = true;
+ if (_deferredBringIntoViewFlag)
+ {
+ _deferredBringIntoViewFlag = false;
+ Dispatcher.UIThread.Post(this.BringIntoView); // must use the Dispatcher, otherwise the TreeView doesn't scroll
+ }
}
private static int CalculateDistanceFromLogicalParent(ILogical? logical, int @default = -1) where T : class
diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs
index aabfd3ef18..07d877142e 100644
--- a/src/Avalonia.Controls/Viewbox.cs
+++ b/src/Avalonia.Controls/Viewbox.cs
@@ -42,6 +42,7 @@ namespace Avalonia.Controls
// can be applied independently of the Viewbox and Child transforms.
_containerVisual = new ViewboxContainer();
_containerVisual.RenderTransformOrigin = RelativePoint.TopLeft;
+ ((ISetLogicalParent)_containerVisual).SetParent(this);
VisualChildren.Add(_containerVisual);
}
diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
index b16ee84b4d..fe694b5730 100644
--- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
+++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
@@ -19,7 +19,8 @@
-
+
+
diff --git a/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj b/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
index 599d95e67a..e784bda105 100644
--- a/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
+++ b/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
@@ -4,6 +4,7 @@
true
+
false
all
diff --git a/src/Avalonia.MicroCom/CallbackBase.cs b/src/Avalonia.MicroCom/CallbackBase.cs
index 6783ebe3dc..49d589a5fc 100644
--- a/src/Avalonia.MicroCom/CallbackBase.cs
+++ b/src/Avalonia.MicroCom/CallbackBase.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.MicroCom
+using MicroCom.Runtime;
+
+namespace Avalonia.MicroCom
{
public abstract class CallbackBase : IUnknown, IMicroComShadowContainer
{
diff --git a/src/Avalonia.MicroCom/IMicroComExceptionCallback.cs b/src/Avalonia.MicroCom/IMicroComExceptionCallback.cs
deleted file mode 100644
index 08f20339ec..0000000000
--- a/src/Avalonia.MicroCom/IMicroComExceptionCallback.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace Avalonia.MicroCom
-{
- public interface IMicroComExceptionCallback
- {
- void RaiseException(Exception e);
- }
-}
diff --git a/src/Avalonia.MicroCom/IMicroComShadowContainer.cs b/src/Avalonia.MicroCom/IMicroComShadowContainer.cs
deleted file mode 100644
index a33d3a9811..0000000000
--- a/src/Avalonia.MicroCom/IMicroComShadowContainer.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Avalonia.MicroCom
-{
- public interface IMicroComShadowContainer
- {
- MicroComShadow Shadow { get; set; }
- void OnReferencedFromNative();
- void OnUnreferencedFromNative();
- }
-}
diff --git a/src/Avalonia.MicroCom/IUnknown.cs b/src/Avalonia.MicroCom/IUnknown.cs
deleted file mode 100644
index 0dc4106423..0000000000
--- a/src/Avalonia.MicroCom/IUnknown.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System;
-
-namespace Avalonia.MicroCom
-{
- public interface IUnknown : IDisposable
- {
- }
-}
diff --git a/src/Avalonia.MicroCom/LocalInterop.cs b/src/Avalonia.MicroCom/LocalInterop.cs
deleted file mode 100644
index 785f4e03a5..0000000000
--- a/src/Avalonia.MicroCom/LocalInterop.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-namespace Avalonia.MicroCom
-{
- unsafe class LocalInterop
- {
- public static unsafe void CalliStdCallvoid(void* thisObject, void* methodPtr)
- {
- throw null;
- }
-
- public static unsafe int CalliStdCallint(void* thisObject, Guid* guid, IntPtr* ppv, void* methodPtr)
- {
- throw null;
- }
- }
-}
diff --git a/src/Avalonia.MicroCom/MicroComProxyBase.cs b/src/Avalonia.MicroCom/MicroComProxyBase.cs
deleted file mode 100644
index fe8a2bf9cf..0000000000
--- a/src/Avalonia.MicroCom/MicroComProxyBase.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System;
-using System.Runtime.ConstrainedExecution;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Avalonia.MicroCom
-{
- public unsafe class MicroComProxyBase : CriticalFinalizerObject, IUnknown
- {
- private IntPtr _nativePointer;
- private bool _ownsHandle;
- private SynchronizationContext _synchronizationContext;
-
- public IntPtr NativePointer
- {
- get
- {
- if (_nativePointer == IntPtr.Zero)
- throw new ObjectDisposedException(this.GetType().FullName);
- return _nativePointer;
- }
- }
-
- public void*** PPV => (void***)NativePointer;
-
- public MicroComProxyBase(IntPtr nativePointer, bool ownsHandle)
- {
- _nativePointer = nativePointer;
- _ownsHandle = ownsHandle;
- _synchronizationContext = SynchronizationContext.Current;
- if(!_ownsHandle)
- GC.SuppressFinalize(this);
- }
-
- protected virtual int VTableSize => 3;
-
- public void AddRef()
- {
- LocalInterop.CalliStdCallvoid(PPV, (*PPV)[1]);
- }
-
- public void Release()
- {
- LocalInterop.CalliStdCallvoid(PPV, (*PPV)[2]);
- }
-
- public int QueryInterface(Guid guid, out IntPtr ppv)
- {
- IntPtr r = default;
- var rv = LocalInterop.CalliStdCallint(PPV, &guid, &r, (*PPV)[0]);
- ppv = r;
- return rv;
- }
-
- public T QueryInterface() where T : IUnknown
- {
- var guid = MicroComRuntime.GetGuidFor(typeof(T));
- var rv = QueryInterface(guid, out var ppv);
- if (rv == 0)
- return (T)MicroComRuntime.CreateProxyFor(typeof(T), ppv, true);
- throw new COMException("QueryInterface failed", rv);
- }
-
- public bool IsDisposed => _nativePointer == IntPtr.Zero;
-
- protected virtual void Dispose(bool disposing)
- {
- if(_nativePointer == IntPtr.Zero)
- return;
- if (_ownsHandle)
- {
- Release();
- _ownsHandle = false;
- }
- _nativePointer = IntPtr.Zero;
- GC.SuppressFinalize(this);
- }
-
- public void Dispose() => Dispose(true);
-
- public bool OwnsHandle => _ownsHandle;
-
- public void EnsureOwned()
- {
- if (!_ownsHandle)
- {
- GC.ReRegisterForFinalize(true);
- AddRef();
- _ownsHandle = true;
- }
- }
-
- private static readonly SendOrPostCallback _disposeDelegate = DisposeOnContext;
-
- private static void DisposeOnContext(object state)
- {
- (state as MicroComProxyBase)?.Dispose(false);
- }
-
- ~MicroComProxyBase()
- {
- if(!_ownsHandle)
- return;
- if (_synchronizationContext == null)
- Dispose();
- else
- _synchronizationContext.Post(_disposeDelegate, this);
- }
- }
-}
diff --git a/src/Avalonia.MicroCom/MicroComRuntime.cs b/src/Avalonia.MicroCom/MicroComRuntime.cs
deleted file mode 100644
index b9a56a69ba..0000000000
--- a/src/Avalonia.MicroCom/MicroComRuntime.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Runtime.InteropServices;
-
-namespace Avalonia.MicroCom
-{
- public static unsafe class MicroComRuntime
- {
- private static ConcurrentDictionary _vtables = new ConcurrentDictionary();
-
- private static ConcurrentDictionary> _factories =
- new ConcurrentDictionary>();
- private static ConcurrentDictionary _guids = new ConcurrentDictionary();
- private static ConcurrentDictionary _guidsToTypes = new ConcurrentDictionary();
-
- internal static readonly Guid ManagedObjectInterfaceGuid = Guid.Parse("cd7687c0-a9c2-4563-b08e-a399df50c633");
-
- static MicroComRuntime()
- {
- Register(typeof(IUnknown), new Guid("00000000-0000-0000-C000-000000000046"),
- (ppv, owns) => new MicroComProxyBase(ppv, owns));
- RegisterVTable(typeof(IUnknown), MicroComVtblBase.Vtable);
- }
-
- public static void RegisterVTable(Type t, IntPtr vtable)
- {
- _vtables[t] = vtable;
- }
-
- public static void Register(Type t, Guid guid, Func proxyFactory)
- {
- _factories[t] = proxyFactory;
- _guids[t] = guid;
- _guidsToTypes[guid] = t;
- }
-
- public static Guid GetGuidFor(Type type) => _guids[type];
-
- public static T CreateProxyFor(void* pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), new IntPtr(pObject), ownsHandle);
- public static T CreateProxyFor(IntPtr pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), pObject, ownsHandle);
-
- public static T CreateProxyOrNullFor(void* pObject, bool ownsHandle) where T : class =>
- pObject == null ? null : (T)CreateProxyFor(typeof(T), new IntPtr(pObject), ownsHandle);
-
- public static T CreateProxyOrNullFor(IntPtr pObject, bool ownsHandle) where T : class =>
- pObject == IntPtr.Zero ? null : (T)CreateProxyFor(typeof(T), pObject, ownsHandle);
-
- public static object CreateProxyFor(Type type, IntPtr pObject, bool ownsHandle) => _factories[type](pObject, ownsHandle);
-
- public static IntPtr GetNativeIntPtr(this T obj, bool owned = false) where T : IUnknown
- => new IntPtr(GetNativePointer(obj, owned));
- public static void* GetNativePointer(T obj, bool owned = false) where T : IUnknown
- {
- if (obj == null)
- return null;
- if (obj is MicroComProxyBase proxy)
- {
- if(owned)
- proxy.AddRef();
- return (void*)proxy.NativePointer;
- }
-
- if (obj is IMicroComShadowContainer container)
- {
- container.Shadow ??= new MicroComShadow(container);
- void* ptr = null;
- var res = container.Shadow.GetOrCreateNativePointer(typeof(T), &ptr);
- if (res != 0)
- throw new COMException(
- "Unable to create native callable wrapper for type " + typeof(T) + " for instance of type " +
- obj.GetType(),
- res);
- if (owned)
- container.Shadow.AddRef((Ccw*)ptr);
- return ptr;
- }
- throw new ArgumentException("Unable to get a native pointer for " + obj);
- }
-
- public static object GetObjectFromCcw(IntPtr ccw)
- {
- var ptr = (Ccw*)ccw;
- var shadow = (MicroComShadow)GCHandle.FromIntPtr(ptr->GcShadowHandle).Target;
- return shadow.Target;
- }
-
- public static bool IsComWrapper(IUnknown obj) => obj is MicroComProxyBase;
-
- public static object TryUnwrapManagedObject(IUnknown obj)
- {
- if (obj is not MicroComProxyBase proxy)
- return null;
- if (proxy.QueryInterface(ManagedObjectInterfaceGuid, out _) != 0)
- return null;
- // Successful QueryInterface always increments ref counter
- proxy.Release();
- return GetObjectFromCcw(proxy.NativePointer);
- }
-
- public static bool TryGetTypeForGuid(Guid guid, out Type t) => _guidsToTypes.TryGetValue(guid, out t);
-
- public static bool GetVtableFor(Type type, out IntPtr ptr) => _vtables.TryGetValue(type, out ptr);
-
- public static void UnhandledException(object target, Exception e)
- {
- if (target is IMicroComExceptionCallback cb)
- {
- try
- {
- cb.RaiseException(e);
- }
- catch
- {
- // We've tried
- }
- }
-
- }
-
- public static T CloneReference(this T iface) where T : IUnknown
- {
- var proxy = (MicroComProxyBase)(object)iface;
- var ownedPointer = GetNativePointer(iface, true);
- return CreateProxyFor(ownedPointer, true);
- }
-
- public static T QueryInterface(this IUnknown unknown) where T : IUnknown
- {
- var proxy = (MicroComProxyBase)unknown;
- return proxy.QueryInterface();
- }
-
- public static void UnsafeAddRef(this IUnknown unknown)
- {
- ((MicroComProxyBase)unknown).AddRef();
- }
-
- public static void UnsafeRelease(this IUnknown unknown)
- {
- ((MicroComProxyBase)unknown).Release();
- }
- }
-}
diff --git a/src/Avalonia.MicroCom/MicroComShadow.cs b/src/Avalonia.MicroCom/MicroComShadow.cs
deleted file mode 100644
index 765ff3b9ad..0000000000
--- a/src/Avalonia.MicroCom/MicroComShadow.cs
+++ /dev/null
@@ -1,177 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Avalonia.MicroCom
-{
- public unsafe class MicroComShadow : IDisposable
- {
- private readonly object _lock = new object();
- private readonly Dictionary _shadows = new Dictionary();
- private readonly Dictionary _backShadows = new Dictionary();
- private GCHandle? _handle;
- private volatile int _refCount;
- internal IMicroComShadowContainer Target { get; }
- internal MicroComShadow(IMicroComShadowContainer target)
- {
- Target = target;
- Target.Shadow = this;
- }
-
- internal int QueryInterface(Ccw* ccw, Guid* guid, void** ppv)
- {
- if (MicroComRuntime.TryGetTypeForGuid(*guid, out var type))
- return QueryInterface(type, ppv);
- else if (*guid == MicroComRuntime.ManagedObjectInterfaceGuid)
- {
- ccw->RefCount++;
- *ppv = ccw;
- return 0;
- }
- else
- return unchecked((int)0x80004002u);
- }
-
- internal int QueryInterface(Type type, void** ppv)
- {
- if (!type.IsInstanceOfType(Target))
- return unchecked((int)0x80004002u);
-
- var rv = GetOrCreateNativePointer(type, ppv);
- if (rv == 0)
- AddRef((Ccw*)*ppv);
- return rv;
- }
-
- internal int GetOrCreateNativePointer(Type type, void** ppv)
- {
- if (!MicroComRuntime.GetVtableFor(type, out var vtable))
- return unchecked((int)0x80004002u);
- lock (_lock)
- {
-
- if (_shadows.TryGetValue(type, out var shadow))
- {
- var targetCcw = (Ccw*)shadow;
- AddRef(targetCcw);
- *ppv = targetCcw;
- return 0;
- }
- else
- {
- var intPtr = Marshal.AllocHGlobal(Marshal.SizeOf());
- var targetCcw = (Ccw*)intPtr;
- *targetCcw = default;
- targetCcw->RefCount = 0;
- targetCcw->VTable = vtable;
- if (_handle == null)
- _handle = GCHandle.Alloc(this);
- targetCcw->GcShadowHandle = GCHandle.ToIntPtr(_handle.Value);
- _shadows[type] = intPtr;
- _backShadows[intPtr] = type;
- *ppv = targetCcw;
-
- return 0;
- }
- }
- }
-
- internal int AddRef(Ccw* ccw)
- {
- if (Interlocked.Increment(ref _refCount) == 1)
- {
- try
- {
- Target.OnReferencedFromNative();
- }
- catch (Exception e)
- {
- MicroComRuntime.UnhandledException(Target, e);
- }
- }
-
- return Interlocked.Increment(ref ccw->RefCount);
- }
-
- internal int Release(Ccw* ccw)
- {
- Interlocked.Decrement(ref _refCount);
- var cnt = Interlocked.Decrement(ref ccw->RefCount);
- if (cnt == 0)
- return FreeCcw(ccw);
-
- return cnt;
- }
-
- int FreeCcw(Ccw* ccw)
- {
- lock (_lock)
- {
- // Shadow got resurrected by a call to QueryInterface from another thread
- if (ccw->RefCount != 0)
- return ccw->RefCount;
-
- var intPtr = new IntPtr(ccw);
- var type = _backShadows[intPtr];
- _backShadows.Remove(intPtr);
- _shadows.Remove(type);
- Marshal.FreeHGlobal(intPtr);
- if (_shadows.Count == 0)
- {
- _handle?.Free();
- _handle = null;
- try
- {
- Target.OnUnreferencedFromNative();
- }
- catch(Exception e)
- {
- MicroComRuntime.UnhandledException(Target, e);
- }
- }
- }
-
- return 0;
- }
-
- /*
- Needs to be called to support the following scenario:
- 1) Object created
- 2) Object passed to native code, shadow is created, CCW is created
- 3) Native side has never called AddRef
-
- In that case the GC handle to the shadow object is still alive
- */
-
- public void Dispose()
- {
- lock (_lock)
- {
- List toRemove = null;
- foreach (var kv in _backShadows)
- {
- var ccw = (Ccw*)kv.Key;
- if (ccw->RefCount == 0)
- {
- toRemove ??= new List();
- toRemove.Add(kv.Key);
- }
- }
-
- if(toRemove != null)
- foreach (var intPtr in toRemove)
- FreeCcw((Ccw*)intPtr);
- }
- }
- }
-
- [StructLayout(LayoutKind.Sequential)]
- struct Ccw
- {
- public IntPtr VTable;
- public IntPtr GcShadowHandle;
- public volatile int RefCount;
- public MicroComShadow GetShadow() => (MicroComShadow)GCHandle.FromIntPtr(GcShadowHandle).Target;
- }
-}
diff --git a/src/Avalonia.MicroCom/MicroComVtblBase.cs b/src/Avalonia.MicroCom/MicroComVtblBase.cs
deleted file mode 100644
index 7092f8131a..0000000000
--- a/src/Avalonia.MicroCom/MicroComVtblBase.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-
-namespace Avalonia.MicroCom
-{
- public unsafe class MicroComVtblBase
- {
- private List _methods = new List();
- [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
- private delegate int AddRefDelegate(Ccw* ccw);
-
- [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
- private delegate int QueryInterfaceDelegate(Ccw* ccw, Guid* guid, void** ppv);
-
- public static IntPtr Vtable { get; } = new MicroComVtblBase().CreateVTable();
- public MicroComVtblBase()
- {
- AddMethod((QueryInterfaceDelegate)QueryInterface);
- AddMethod((AddRefDelegate)AddRef);
- AddMethod((AddRefDelegate)Release);
- }
-
- protected void AddMethod(void* f)
- {
- _methods.Add(new IntPtr(f));
- }
-
- protected void AddMethod(Delegate d)
- {
- GCHandle.Alloc(d);
- _methods.Add(Marshal.GetFunctionPointerForDelegate(d));
- }
-
- protected unsafe IntPtr CreateVTable()
- {
- var ptr = (IntPtr*)Marshal.AllocHGlobal((IntPtr.Size + 1) * _methods.Count);
- for (var c = 0; c < _methods.Count; c++)
- ptr[c] = _methods[c];
- return new IntPtr(ptr);
- }
-
- static int QueryInterface(Ccw* ccw, Guid* guid, void** ppv) => ccw->GetShadow().QueryInterface(ccw, guid, ppv);
- static int AddRef(Ccw* ccw) => ccw->GetShadow().AddRef(ccw);
- static int Release(Ccw* ccw) => ccw->GetShadow().Release(ccw);
- }
-}
diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj
index 2001a2fcbc..4ceb1be340 100644
--- a/src/Avalonia.Native/Avalonia.Native.csproj
+++ b/src/Avalonia.Native/Avalonia.Native.csproj
@@ -6,7 +6,6 @@
true
net6.0;netstandard2.0
true
- Avalonia.MicroCom
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index b45fe5559b..532893884a 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -10,6 +10,7 @@ using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
using JetBrains.Annotations;
+using MicroCom.Runtime;
namespace Avalonia.Native
{
diff --git a/src/Avalonia.Native/CallbackBase.cs b/src/Avalonia.Native/CallbackBase.cs
index 56f9505cb4..2d875dbc0e 100644
--- a/src/Avalonia.Native/CallbackBase.cs
+++ b/src/Avalonia.Native/CallbackBase.cs
@@ -2,6 +2,7 @@
using System.Runtime.ExceptionServices;
using Avalonia.MicroCom;
using Avalonia.Platform;
+using MicroCom.Runtime;
namespace Avalonia.Native
{
diff --git a/src/Avalonia.Native/NativeControlHostImpl.cs b/src/Avalonia.Native/NativeControlHostImpl.cs
index 2c9c1728d3..8a3488d95e 100644
--- a/src/Avalonia.Native/NativeControlHostImpl.cs
+++ b/src/Avalonia.Native/NativeControlHostImpl.cs
@@ -4,6 +4,7 @@ using Avalonia.MicroCom;
using Avalonia.Native.Interop;
using Avalonia.Platform;
using Avalonia.VisualTree;
+using MicroCom.Runtime;
namespace Avalonia.Native
{
diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs
index 83db2e8a28..53bd12cde1 100644
--- a/src/Avalonia.Native/ScreenImpl.cs
+++ b/src/Avalonia.Native/ScreenImpl.cs
@@ -30,10 +30,10 @@ namespace Avalonia.Native
var screen = _native.GetScreen(i);
result[i] = new Screen(
- screen.PixelDensity,
+ screen.Scaling,
screen.Bounds.ToAvaloniaPixelRect(),
screen.WorkingArea.ToAvaloniaPixelRect(),
- screen.Primary.FromComBool());
+ screen.IsPrimary.FromComBool());
}
return result;
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 34de439c94..381741cea9 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -92,7 +92,7 @@ namespace Avalonia.Native
_savedScaling = RenderScaling;
_nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost());
- var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
+ var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
.FirstOrDefault(m => m.Bounds.Contains(Position));
Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), PlatformResizeReason.Layout);
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index a98d213887..b0bff07146 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/src/Avalonia.Native/avn.idl
@@ -256,8 +256,8 @@ struct AvnScreen
{
AvnRect Bounds;
AvnRect WorkingArea;
- float PixelDensity;
- bool Primary;
+ float Scaling;
+ bool IsPrimary;
}
enum AvnPixelFormat
diff --git a/src/Avalonia.X11/X11Screens.cs b/src/Avalonia.X11/X11Screens.cs
index bcaafb6a53..ba6029b350 100644
--- a/src/Avalonia.X11/X11Screens.cs
+++ b/src/Avalonia.X11/X11Screens.cs
@@ -9,7 +9,7 @@ using JetBrains.Annotations;
namespace Avalonia.X11
{
- class X11Screens : IScreenImpl
+ class X11Screens : IScreenImpl
{
private IX11Screens _impl;
@@ -218,7 +218,7 @@ namespace Avalonia.X11
public int ScreenCount => _impl.Screens.Length;
public IReadOnlyList AllScreens =>
- _impl.Screens.Select(s => new Screen(s.PixelDensity, s.Bounds, s.WorkingArea, s.Primary)).ToArray();
+ _impl.Screens.Select(s => new Screen(s.Scaling, s.Bounds, s.WorkingArea, s.IsPrimary)).ToArray();
}
interface IX11Screens
@@ -281,30 +281,34 @@ namespace Avalonia.X11
{
private const int FullHDWidth = 1920;
private const int FullHDHeight = 1080;
- public bool Primary { get; }
+ public bool IsPrimary { get; }
public string Name { get; set; }
public PixelRect Bounds { get; set; }
public Size? PhysicalSize { get; set; }
- public double PixelDensity { get; set; }
+ public double Scaling { get; set; }
public PixelRect WorkingArea { get; set; }
- public X11Screen(PixelRect bounds, bool primary,
- string name, Size? physicalSize, double? pixelDensity)
+ public X11Screen(
+ PixelRect bounds,
+ bool isPrimary,
+ string name,
+ Size? physicalSize,
+ double? scaling)
{
- Primary = primary;
+ IsPrimary = isPrimary;
Name = name;
Bounds = bounds;
- if (physicalSize == null && pixelDensity == null)
+ if (physicalSize == null && scaling == null)
{
- PixelDensity = 1;
+ Scaling = 1;
}
- else if (pixelDensity == null)
+ else if (scaling == null)
{
- PixelDensity = GuessPixelDensity(bounds, physicalSize.Value);
+ Scaling = GuessPixelDensity(bounds, physicalSize.Value);
}
else
{
- PixelDensity = pixelDensity.Value;
+ Scaling = scaling.Value;
PhysicalSize = physicalSize;
}
}
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index f24c33cafa..690ac0ebce 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -120,7 +120,7 @@ namespace Avalonia.X11
if (!_popup && Screen != null)
{
- var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
+ var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
.FirstOrDefault(m => m.Bounds.Contains(Position));
if (monitor != null)
@@ -570,9 +570,9 @@ namespace Avalonia.X11
newScaling = _scalingOverride.Value;
else
{
- var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity)
+ var monitor = _platform.X11Screens.Screens.OrderBy(x => x.Scaling)
.FirstOrDefault(m => m.Bounds.Contains(Position));
- newScaling = monitor?.PixelDensity ?? RenderScaling;
+ newScaling = monitor?.Scaling ?? RenderScaling;
}
if (RenderScaling != newScaling)
@@ -994,7 +994,7 @@ namespace Avalonia.X11
public IScreenImpl Screen => _platform.Screens;
- public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity))
+ public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.Scaling))
.OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
index f9be3fd62a..1dc7ce5e99 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -5,7 +5,6 @@
true
Avalonia.Markup.Xaml.Loader
$(DefineConstants);XAMLX_INTERNAL
- 11
diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
index 6b25cbbeab..6711c3dd3d 100644
--- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
+++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
@@ -2,7 +2,6 @@
net6.0;netstandard2.0
Avalonia
- 11
diff --git a/src/Web/Avalonia.Web/Avalonia.Web.props b/src/Web/Avalonia.Web/Avalonia.Web.props
index 6c975cd284..668dd20789 100644
--- a/src/Web/Avalonia.Web/Avalonia.Web.props
+++ b/src/Web/Avalonia.Web/Avalonia.Web.props
@@ -1,5 +1,5 @@
-
+
- $(EmccExtraLDFlags) --js-library="$(MSBuildThisFileDirectory)\interop.js"
+ 16384000
diff --git a/src/Web/Avalonia.Web/Avalonia.Web.targets b/src/Web/Avalonia.Web/Avalonia.Web.targets
index d1bec2aa93..b6a09b33ef 100644
--- a/src/Web/Avalonia.Web/Avalonia.Web.targets
+++ b/src/Web/Avalonia.Web/Avalonia.Web.targets
@@ -4,4 +4,34 @@
+
+
+ True
+ $(EmccExtraLDFlags) --js-library="$(MSBuildThisFileDirectory)\interop.js"
+ $(EmccExtraLDFlags) -sERROR_ON_UNDEFINED_SYMBOLS=0
+ true
+
+
+
+ true
+ full
+ true
+ -Oz
+ -Oz
+ false
+ false
+ 0
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ en
+ false
+
diff --git a/src/Web/Avalonia.Web/AvaloniaView.cs b/src/Web/Avalonia.Web/AvaloniaView.cs
index 3a31679424..098b06a0a2 100644
--- a/src/Web/Avalonia.Web/AvaloniaView.cs
+++ b/src/Web/Avalonia.Web/AvaloniaView.cs
@@ -77,8 +77,7 @@ namespace Avalonia.Web
_topLevelImpl.SetCssCursor = (cursor) =>
{
- InputHelper.SetCursor(_containerElement, cursor); // macOS
- InputHelper.SetCursor(_canvas, cursor); // windows
+ InputHelper.SetCursor(_containerElement, cursor);
};
_topLevel.Prepare();
@@ -136,6 +135,8 @@ namespace Avalonia.Web
DomHelper.ObserveSize(host, null, OnSizeChanged);
CanvasHelper.RequestAnimationFrame(_canvas, true);
+
+ InputHelper.FocusElement(_containerElement);
}
private static RawPointerPoint ExtractRawPointerFromJSArgs(JSObject args)
@@ -252,7 +253,14 @@ namespace Avalonia.Web
private bool OnKeyDown (string code, string key, int modifier)
{
- return _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyDown, code, key, (RawInputModifiers)modifier);
+ var handled = _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyDown, code, key, (RawInputModifiers)modifier);
+
+ if (!handled && key.Length == 1)
+ {
+ handled = _topLevelImpl.RawTextEvent(key);
+ }
+
+ return handled;
}
private bool OnKeyUp(string code, string key, int modifier)
diff --git a/src/Web/Avalonia.Web/Interop/CanvasHelper.cs b/src/Web/Avalonia.Web/Interop/CanvasHelper.cs
index efa94916fa..5bbe503bc1 100644
--- a/src/Web/Avalonia.Web/Interop/CanvasHelper.cs
+++ b/src/Web/Avalonia.Web/Interop/CanvasHelper.cs
@@ -33,7 +33,7 @@ internal static partial class CanvasHelper
public static partial void RequestAnimationFrame(JSObject canvas, bool renderLoop);
[JSImport("Canvas.setCanvasSize", AvaloniaModule.MainModuleName)]
- public static partial void SetCanvasSize(JSObject canvas, int height, int width);
+ public static partial void SetCanvasSize(JSObject canvas, int width, int height);
[JSImport("Canvas.initGL", AvaloniaModule.MainModuleName)]
private static partial JSObject InitGL(
diff --git a/src/Web/Avalonia.Web/webapp/build.js b/src/Web/Avalonia.Web/webapp/build.js
index 6b6df4c300..81f863cac7 100644
--- a/src/Web/Avalonia.Web/webapp/build.js
+++ b/src/Web/Avalonia.Web/webapp/build.js
@@ -5,7 +5,7 @@ require("esbuild").build({
],
outdir: "../wwwroot",
bundle: true,
- minify: false,
+ minify: true,
format: "esm",
target: "es2016",
platform: "browser",
diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts
index 9ae9b3d2a8..47c501cbb7 100644
--- a/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts
+++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts
@@ -106,12 +106,6 @@ export class Canvas {
// add the draw to the next frame
this.renderLoopRequest = window.requestAnimationFrame(() => {
- if (this.glInfo) {
- const GL = (globalThis as any).AvaloniaGL;
- // make current
- GL.makeContextCurrent(this.glInfo.context);
- }
-
if (this.htmlCanvas.width !== this.newWidth) {
this.htmlCanvas.width = this.newWidth ?? 0;
}
@@ -131,6 +125,11 @@ export class Canvas {
}
public setCanvasSize(width: number, height: number): void {
+ if (this.renderLoopRequest !== 0) {
+ window.cancelAnimationFrame(this.renderLoopRequest);
+ this.renderLoopRequest = 0;
+ }
+
this.newWidth = width;
this.newHeight = height;
@@ -142,11 +141,7 @@ export class Canvas {
this.htmlCanvas.height = this.newHeight;
}
- if (this.glInfo) {
- const GL = (globalThis as any).AvaloniaGL;
- // make current
- GL.makeContextCurrent(this.glInfo.context);
- }
+ this.requestAnimationFrame();
}
public static setCanvasSize(element: HTMLCanvasElement, width: number, height: number): void {
@@ -210,23 +205,25 @@ interface SizeWatcherInstance {
export class SizeWatcher {
static observer: ResizeObserver;
static elements: Map;
+ private static lastMove: number;
public static observe(element: HTMLElement, elementId: string | undefined, callback: (width: number, height: number) => void): void {
if (!element || !callback) {
return;
}
- SizeWatcher.init();
+ SizeWatcher.lastMove = Date.now();
- const watcherElement = element as SizeWatcherElement;
- watcherElement.SizeWatcher = {
- callback
- };
+ callback(element.clientWidth, element.clientHeight);
- SizeWatcher.elements.set(elementId ?? element.id, element);
- SizeWatcher.observer.observe(element);
+ const handleResize = (args: UIEvent) => {
+ if (Date.now() - SizeWatcher.lastMove > 33) {
+ callback(element.clientWidth, element.clientHeight);
+ SizeWatcher.lastMove = Date.now();
+ }
+ };
- SizeWatcher.invoke(element);
+ window.addEventListener("resize", handleResize);
}
public static unobserve(elementId: string): void {
diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts
index 2257d56a92..385cdd4c41 100644
--- a/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts
+++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts
@@ -11,6 +11,7 @@ export class AvaloniaDOM {
host.tabIndex = 0;
host.oncontextmenu = function () { return false; };
host.style.overflow = "hidden";
+ host.style.touchAction = "none";
// Rendering target canvas
const canvas = document.createElement("canvas");
@@ -18,12 +19,11 @@ export class AvaloniaDOM {
canvas.classList.add("avalonia-canvas");
canvas.style.backgroundColor = "#ccc";
canvas.style.width = "100%";
- canvas.style.height = "100%";
canvas.style.position = "absolute";
// Native controls host
const nativeHost = document.createElement("div");
- canvas.id = `nativeHost${randomIdPart}`;
+ nativeHost.id = `nativeHost${randomIdPart}`;
nativeHost.classList.add("avalonia-native-host");
nativeHost.style.left = "0px";
nativeHost.style.top = "0px";
@@ -33,7 +33,7 @@ export class AvaloniaDOM {
// IME
const inputElement = document.createElement("input");
- canvas.id = `input${randomIdPart}`;
+ inputElement.id = `inputElement${randomIdPart}`;
inputElement.classList.add("avalonia-input-element");
inputElement.autocapitalize = "none";
inputElement.type = "text";
diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts
index 768414ccab..faede82e0d 100644
--- a/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts
+++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts
@@ -98,27 +98,23 @@ export class InputHelper {
wheelCallback: (args: WheelEvent) => boolean
) {
const pointerMoveHandler = (args: PointerEvent) => {
- if (pointerMoveCallback(args)) {
- args.preventDefault();
- }
+ pointerMoveCallback(args);
+ args.preventDefault();
};
const pointerDownHandler = (args: PointerEvent) => {
- if (pointerDownCallback(args)) {
- args.preventDefault();
- }
+ pointerDownCallback(args);
+ args.preventDefault();
};
const pointerUpHandler = (args: PointerEvent) => {
- if (pointerUpCallback(args)) {
- args.preventDefault();
- }
+ pointerUpCallback(args);
+ args.preventDefault();
};
const wheelHandler = (args: WheelEvent) => {
- if (wheelCallback(args)) {
- args.preventDefault();
- }
+ wheelCallback(args);
+ args.preventDefault();
};
element.addEventListener("pointermove", pointerMoveHandler);
@@ -159,7 +155,11 @@ export class InputHelper {
}
public static setCursor(inputElement: HTMLInputElement, kind: string) {
- inputElement.style.cursor = kind;
+ if (kind === "pointer") {
+ inputElement.style.removeProperty("cursor");
+ } else {
+ inputElement.style.cursor = kind;
+ }
}
public static setBounds(inputElement: HTMLInputElement, x: number, y: number, caretWidth: number, caretHeight: number, caret: number) {
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index 1d265369fd..0d29bb91ea 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -3,7 +3,6 @@
net6.0;netstandard2.0
true
Avalonia.Win32
- Avalonia.MicroCom
diff --git a/src/Windows/Avalonia.Win32/ClipboardImpl.cs b/src/Windows/Avalonia.Win32/ClipboardImpl.cs
index 7cf8b14bed..7e058cb052 100644
--- a/src/Windows/Avalonia.Win32/ClipboardImpl.cs
+++ b/src/Windows/Avalonia.Win32/ClipboardImpl.cs
@@ -7,6 +7,7 @@ using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
+using MicroCom.Runtime;
namespace Avalonia.Win32
{
@@ -83,7 +84,7 @@ namespace Avalonia.Win32
while (true)
{
- var ptr = MicroCom.MicroComRuntime.GetNativeIntPtr(wrapper);
+ var ptr = wrapper.GetNativeIntPtr();
var hr = UnmanagedMethods.OleSetClipboard(ptr);
if (hr == 0)
@@ -107,7 +108,7 @@ namespace Avalonia.Win32
if (hr == 0)
{
- using var proxy = MicroCom.MicroComRuntime.CreateProxyFor(dataObject, true);
+ using var proxy = MicroComRuntime.CreateProxyFor(dataObject, true);
using var wrapper = new OleDataObject(proxy);
var formats = wrapper.GetDataFormats().ToArray();
return formats;
@@ -131,7 +132,7 @@ namespace Avalonia.Win32
if (hr == 0)
{
- using var proxy = MicroCom.MicroComRuntime.CreateProxyFor(dataObject, true);
+ using var proxy = MicroComRuntime.CreateProxyFor(dataObject, true);
using var wrapper = new OleDataObject(proxy);
var rv = wrapper.Get(format);
return rv;
diff --git a/src/Windows/Avalonia.Win32/DragSource.cs b/src/Windows/Avalonia.Win32/DragSource.cs
index 1159c5bfc9..53d4345c5e 100644
--- a/src/Windows/Avalonia.Win32/DragSource.cs
+++ b/src/Windows/Avalonia.Win32/DragSource.cs
@@ -3,6 +3,7 @@ using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
+using MicroCom.Runtime;
namespace Avalonia.Win32
{
@@ -19,8 +20,8 @@ namespace Avalonia.Win32
using var src = new OleDragSource();
var allowed = OleDropTarget.ConvertDropEffect(allowedEffects);
- var objPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(dataObject);
- var srcPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(src);
+ var objPtr = MicroComRuntime.GetNativeIntPtr(dataObject);
+ var srcPtr = MicroComRuntime.GetNativeIntPtr(src);
UnmanagedMethods.DoDragDrop(objPtr, srcPtr, (int)allowed, out var finalEffect);
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index 9d1920498b..f1ed53ea99 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -7,6 +7,7 @@ using System.Runtime.InteropServices.ComTypes;
using System.Text;
using Avalonia.MicroCom;
+using MicroCom.Runtime;
using Avalonia.Win32.Win32Com;
// ReSharper disable InconsistentNaming
@@ -1877,7 +1878,10 @@ namespace Avalonia.Win32.Interop
public static uint LGID(IntPtr HKL)
{
- return (uint)(HKL.ToInt32() & 0xffff);
+ unchecked
+ {
+ return (uint)((ulong)HKL & 0xffff);
+ }
}
public const int SORT_DEFAULT = 0;
diff --git a/src/Windows/Avalonia.Win32/OleContext.cs b/src/Windows/Avalonia.Win32/OleContext.cs
index c025d06fe7..e41423a334 100644
--- a/src/Windows/Avalonia.Win32/OleContext.cs
+++ b/src/Windows/Avalonia.Win32/OleContext.cs
@@ -5,6 +5,7 @@ using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
using Avalonia.Win32.Win32Com;
+using MicroCom.Runtime;
namespace Avalonia.Win32
{
@@ -47,7 +48,7 @@ namespace Avalonia.Win32
return false;
}
- var trgPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(target);
+ var trgPtr = target.GetNativeIntPtr();
return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, trgPtr) == UnmanagedMethods.HRESULT.S_OK;
}
diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs
index f7345b3ff7..90992d803f 100644
--- a/src/Windows/Avalonia.Win32/OleDataObject.cs
+++ b/src/Windows/Avalonia.Win32/OleDataObject.cs
@@ -10,7 +10,7 @@ using Avalonia.Input;
using Avalonia.MicroCom;
using Avalonia.Utilities;
using Avalonia.Win32.Interop;
-
+using MicroCom.Runtime;
using IDataObject = Avalonia.Input.IDataObject;
namespace Avalonia.Win32
diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs
index 3d0d35228c..1c22fd025d 100644
--- a/src/Windows/Avalonia.Win32/OleDropTarget.cs
+++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs
@@ -5,6 +5,7 @@ using Avalonia.Input.Raw;
using Avalonia.MicroCom;
using Avalonia.Platform;
using Avalonia.Win32.Interop;
+using MicroCom.Runtime;
using DropEffect = Avalonia.Win32.Win32Com.DropEffect;
namespace Avalonia.Win32
diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs
index 346d6e5adb..8d565d7fef 100644
--- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs
+++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs
@@ -216,7 +216,7 @@ namespace Avalonia.Win32
{
Anchor = PopupAnchor.TopLeft,
Gravity = PopupGravity.BottomRight,
- AnchorRectangle = new Rect(Position.ToPoint(1) / Screens.Primary.PixelDensity, new Size(1, 1)),
+ AnchorRectangle = new Rect(Position.ToPoint(1) / Screens.Primary.Scaling, new Size(1, 1)),
Size = finalRect.Size,
ConstraintAdjustment = PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY,
});
@@ -244,16 +244,16 @@ namespace Avalonia.Win32
{
var point = _hiddenWindow.Screens.Primary.Bounds.TopLeft;
var size = _hiddenWindow.Screens.Primary.Bounds.Size;
- return new Rect(point.X, point.Y, size.Width * _hiddenWindow.Screens.Primary.PixelDensity, size.Height * _hiddenWindow.Screens.Primary.PixelDensity);
+ return new Rect(point.X, point.Y, size.Width * _hiddenWindow.Screens.Primary.Scaling, size.Height * _hiddenWindow.Screens.Primary.Scaling);
}
}
public void MoveAndResize(Point devicePoint, Size virtualSize)
{
- _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _hiddenWindow.Screens.Primary.PixelDensity);
+ _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _hiddenWindow.Screens.Primary.Scaling);
}
- public double Scaling => _hiddenWindow.Screens.Primary.PixelDensity;
+ public double Scaling => _hiddenWindow.Screens.Primary.Scaling;
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32StorageProvider.cs b/src/Windows/Avalonia.Win32/Win32StorageProvider.cs
index bb1de56f2b..8cc0a380f9 100644
--- a/src/Windows/Avalonia.Win32/Win32StorageProvider.cs
+++ b/src/Windows/Avalonia.Win32/Win32StorageProvider.cs
@@ -12,6 +12,7 @@ using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO;
using Avalonia.Win32.Interop;
using Avalonia.Win32.Win32Com;
+using MicroCom.Runtime;
namespace Avalonia.Win32
{
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs
index a09918a3a6..ef3de9fbe1 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs
@@ -6,6 +6,7 @@ using Avalonia.MicroCom;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.Win32.Interop;
+using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition
{
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
index 9a6bd9572a..8a41c00add 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
@@ -12,6 +12,7 @@ using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.Rendering;
using Avalonia.Win32.Interop;
+using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition
{
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs
index ea75a2f311..32f44842f8 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs
@@ -2,6 +2,7 @@
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.MicroCom;
+using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition
{
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
index 4ed882552b..fc04dcda26 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
@@ -6,6 +6,7 @@ using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Utilities;
using Avalonia.Win32.Interop;
+using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition
{
diff --git a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs
index 89cde01ff4..1794b022fe 100644
--- a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs
@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.MicroCom;
using Avalonia.Win32.Interop;
+using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT
{
diff --git a/src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs b/src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs
index d2ec957b8e..1fde57fe38 100644
--- a/src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs
@@ -3,6 +3,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Avalonia.MicroCom;
+using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT
{
diff --git a/src/Windows/Avalonia.Win32/WinScreen.cs b/src/Windows/Avalonia.Win32/WinScreen.cs
index f103cc3b66..1038f41a17 100644
--- a/src/Windows/Avalonia.Win32/WinScreen.cs
+++ b/src/Windows/Avalonia.Win32/WinScreen.cs
@@ -7,7 +7,8 @@ namespace Avalonia.Win32
{
private readonly IntPtr _hMonitor;
- public WinScreen(double pixelDensity, PixelRect bounds, PixelRect workingArea, bool primary, IntPtr hMonitor) : base(pixelDensity, bounds, workingArea, primary)
+ public WinScreen(double scaling, PixelRect bounds, PixelRect workingArea, bool isPrimary, IntPtr hMonitor)
+ : base(scaling, bounds, workingArea, isPrimary)
{
_hMonitor = hMonitor;
}
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 0f243fcf9f..5374614379 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -224,7 +224,7 @@ namespace Avalonia.Win32
}
}
- private double PrimaryScreenRenderScaling => Screen.AllScreens.FirstOrDefault(screen => screen.Primary)?.PixelDensity ?? 1;
+ private double PrimaryScreenRenderScaling => Screen.AllScreens.FirstOrDefault(screen => screen.IsPrimary)?.Scaling ?? 1;
public double RenderScaling => _scaling;
diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs
index 4ffd314857..629408bcba 100644
--- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs
@@ -1,4 +1,5 @@
using Avalonia.Controls.Shapes;
+using Avalonia.Data;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.UnitTests;
@@ -207,6 +208,26 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Size(200, 200), target.DesiredSize);
}
+ [Fact]
+ public void Child_DataContext_Binding_Works()
+ {
+ var data = new
+ {
+ Foo = "foo",
+ };
+
+ var target = new Viewbox()
+ {
+ DataContext = data,
+ Child = new Canvas
+ {
+ [!Canvas.DataContextProperty] = new Binding("Foo"),
+ },
+ };
+
+ Assert.Equal("foo", target.Child.DataContext);
+ }
+
private bool TryGetScale(Viewbox viewbox, out Vector scale)
{
if (viewbox.InternalTransform is null)