Browse Source

Merge branch 'master' into refactor/new-value-store

pull/8600/head
Steven Kirk 4 years ago
committed by GitHub
parent
commit
4c436ace3f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/FUNDING.yml
  2. 2
      build/SharedVersion.props
  3. 4
      native/Avalonia.Native/src/OSX/Screens.mm
  4. 5
      samples/ControlCatalog.Web/ControlCatalog.Web.csproj
  5. 1
      samples/ControlCatalog.Web/Roots.xml
  6. 4
      samples/ControlCatalog/Pages/ScreenPage.cs
  7. 2
      src/Avalonia.Base/Input/DragDrop.cs
  8. 8
      src/Avalonia.Base/Input/DragDropDevice.cs
  9. 4
      src/Avalonia.Base/Media/PathMarkupParser.cs
  10. 6
      src/Avalonia.Controls/Platform/IScreenImpl.cs
  11. 57
      src/Avalonia.Controls/Platform/Screen.cs
  12. 23
      src/Avalonia.Controls/Screens.cs
  13. 30
      src/Avalonia.Controls/TreeViewItem.cs
  14. 1
      src/Avalonia.Controls/Viewbox.cs
  15. 3
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  16. 1
      src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
  17. 4
      src/Avalonia.MicroCom/CallbackBase.cs
  18. 9
      src/Avalonia.MicroCom/IMicroComExceptionCallback.cs
  19. 9
      src/Avalonia.MicroCom/IMicroComShadowContainer.cs
  20. 8
      src/Avalonia.MicroCom/IUnknown.cs
  21. 17
      src/Avalonia.MicroCom/LocalInterop.cs
  22. 110
      src/Avalonia.MicroCom/MicroComProxyBase.cs
  23. 143
      src/Avalonia.MicroCom/MicroComRuntime.cs
  24. 177
      src/Avalonia.MicroCom/MicroComShadow.cs
  25. 47
      src/Avalonia.MicroCom/MicroComVtblBase.cs
  26. 1
      src/Avalonia.Native/Avalonia.Native.csproj
  27. 1
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  28. 1
      src/Avalonia.Native/CallbackBase.cs
  29. 1
      src/Avalonia.Native/NativeControlHostImpl.cs
  30. 4
      src/Avalonia.Native/ScreenImpl.cs
  31. 2
      src/Avalonia.Native/WindowImplBase.cs
  32. 4
      src/Avalonia.Native/avn.idl
  33. 28
      src/Avalonia.X11/X11Screens.cs
  34. 8
      src/Avalonia.X11/X11Window.cs
  35. 1
      src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
  36. 1
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  37. 4
      src/Web/Avalonia.Web/Avalonia.Web.props
  38. 30
      src/Web/Avalonia.Web/Avalonia.Web.targets
  39. 14
      src/Web/Avalonia.Web/AvaloniaView.cs
  40. 2
      src/Web/Avalonia.Web/Interop/CanvasHelper.cs
  41. 2
      src/Web/Avalonia.Web/webapp/build.js
  42. 35
      src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts
  43. 6
      src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts
  44. 26
      src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts
  45. 1
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  46. 7
      src/Windows/Avalonia.Win32/ClipboardImpl.cs
  47. 5
      src/Windows/Avalonia.Win32/DragSource.cs
  48. 6
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  49. 3
      src/Windows/Avalonia.Win32/OleContext.cs
  50. 2
      src/Windows/Avalonia.Win32/OleDataObject.cs
  51. 1
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  52. 8
      src/Windows/Avalonia.Win32/TrayIconImpl.cs
  53. 1
      src/Windows/Avalonia.Win32/Win32StorageProvider.cs
  54. 1
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs
  55. 1
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
  56. 1
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs
  57. 1
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
  58. 1
      src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs
  59. 1
      src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs
  60. 3
      src/Windows/Avalonia.Win32/WinScreen.cs
  61. 2
      src/Windows/Avalonia.Win32/WindowImpl.cs
  62. 21
      tests/Avalonia.Controls.UnitTests/ViewboxTests.cs

1
.github/FUNDING.yml

@ -1 +1,2 @@
github: avaloniaui
open_collective: avalonia

2
build/SharedVersion.props

@ -8,7 +8,7 @@
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>CS1591</NoWarn>
<LangVersion>latest</LangVersion>
<LangVersion>preview</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon>
<PackageDescription>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.</PackageDescription>

4
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;
}

5
samples/ControlCatalog.Web/ControlCatalog.Web.csproj

@ -16,9 +16,8 @@
<TrimMode>full</TrimMode>
<WasmBuildNative>true</WasmBuildNative>
<InvariantGlobalization>true</InvariantGlobalization>
<WasmEnableSIMD>true</WasmEnableSIMD>
<EmccCompileOptimizationFlag>-O3</EmccCompileOptimizationFlag>
<EmccLinkOptimizationFlag>-O3</EmccLinkOptimizationFlag>
<EmccCompileOptimizationFlag>-O2</EmccCompileOptimizationFlag>
<EmccLinkOptimizationFlag>-O2</EmccLinkOptimizationFlag>
</PropertyGroup>
<ItemGroup>

1
samples/ControlCatalog.Web/Roots.xml

@ -3,4 +3,5 @@
<assembly fullname="ControlCatalog.Web" preserve="All" />
<assembly fullname="Avalonia.Themes.Fluent" preserve="All" />
<assembly fullname="Avalonia.Themes.Simple" preserve="All" />
<assembly fullname="Avalonia.Controls.ColorPicker" preserve="All" />
</linker>

4
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 =

2
src/Avalonia.Base/Input/DragDrop.cs

@ -13,7 +13,7 @@ namespace Avalonia.Input
/// <summary>
/// Event which is raised, when a drag-and-drop operation leaves the element.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> DragLeaveEvent = RoutedEvent.Register<RoutedEventArgs>("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
public static readonly RoutedEvent<DragEventArgs> DragLeaveEvent = RoutedEvent.Register<DragEventArgs>("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
/// <summary>
/// Event which is raised, when a drag-and-drop operation is updated while over the element.
/// </summary>

8
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);

4
src/Avalonia.Base/Media/PathMarkupParser.cs

@ -188,7 +188,7 @@ namespace Avalonia.Media
_isOpen = true;
}
private void SetFillRule(ref ReadOnlySpan<char> span)
private void SetFillRule(scoped ref ReadOnlySpan<char> 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<char> remaining, out ReadOnlySpan<char> argument)
private static bool ReadArgument(scoped ref ReadOnlySpan<char> remaining, out ReadOnlySpan<char> argument)
{
remaining = SkipWhitespace(remaining);
if (remaining.IsEmpty)

6
src/Avalonia.Controls/Platform/IScreenImpl.cs

@ -6,8 +6,14 @@ namespace Avalonia.Platform
[Unstable]
public interface IScreenImpl
{
/// <summary>
/// Gets the total number of screens available on the device.
/// </summary>
int ScreenCount { get; }
/// <summary>
/// Gets the list of all screens available on the device.
/// </summary>
IReadOnlyList<Screen> AllScreens { get; }
Screen? ScreenFromWindow(IWindowBaseImpl window);

57
src/Avalonia.Controls/Platform/Screen.cs

@ -1,21 +1,64 @@
namespace Avalonia.Platform
using System;
namespace Avalonia.Platform
{
/// <summary>
/// Represents a single display screen.
/// </summary>
public class Screen
{
public double PixelDensity { get; }
/// <summary>
/// Gets the scaling factor applied to the screen by the operating system.
/// </summary>
/// <remarks>
/// Multiply this value by 100 to get a percentage.
/// Both X and Y scaling factors are assumed uniform.
/// </remarks>
public double Scaling { get; }
/// <inheritdoc cref="Scaling"/>
[Obsolete("Use the Scaling property instead.")]
public double PixelDensity => Scaling;
/// <summary>
/// Gets the overall pixel-size of the screen.
/// </summary>
/// <remarks>
/// This generally is the raw pixel counts in both the X and Y direction.
/// </remarks>
public PixelRect Bounds { get; }
/// <summary>
/// Gets the actual working-area pixel-size of the screen.
/// </summary>
/// <remarks>
/// This area may be smaller than <see href="Bounds"/> to account for notches and
/// other block-out areas such as taskbars etc.
/// </remarks>
public PixelRect WorkingArea { get; }
public bool Primary { get; }
public Screen(double pixelDensity, PixelRect bounds, PixelRect workingArea, bool primary)
/// <summary>
/// Gets a value indicating whether the screen is the primary one.
/// </summary>
public bool IsPrimary { get; }
/// <inheritdoc cref="IsPrimary"/>
[Obsolete("Use the IsPrimary property instead.")]
public bool Primary => IsPrimary;
/// <summary>
/// Initializes a new instance of the <see cref="Screen"/> class.
/// </summary>
/// <param name="scaling">The scaling factor applied to the screen by the operating system.</param>
/// <param name="bounds">The overall pixel-size of the screen.</param>
/// <param name="workingArea">The actual working-area pixel-size of the screen.</param>
/// <param name="isPrimary">Whether the screen is the primary one.</param>
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;
}
}
}

23
src/Avalonia.Controls/Screens.cs

@ -8,14 +8,31 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
/// <summary>
/// Represents all screens available on a device.
/// </summary>
public class Screens
{
private readonly IScreenImpl _iScreenImpl;
/// <summary>
/// Gets the total number of screens available on the device.
/// </summary>
public int ScreenCount => _iScreenImpl?.ScreenCount ?? 0;
/// <summary>
/// Gets the list of all screens available on the device.
/// </summary>
public IReadOnlyList<Screen> All => _iScreenImpl?.AllScreens ?? Array.Empty<Screen>();
public Screen? Primary => All.FirstOrDefault(x => x.Primary);
/// <summary>
/// Gets the primary screen on the device.
/// </summary>
public Screen? Primary => All.FirstOrDefault(x => x.IsPrimary);
/// <summary>
/// Initializes a new instance of the <see cref="Screens"/> class.
/// </summary>
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);
}

30
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;
/// <summary>
/// Initializes static members of the <see cref="TreeViewItem"/> 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<IControl>("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<T>(ILogical? logical, int @default = -1) where T : class

1
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);
}

3
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -19,7 +19,8 @@
<ProjectReference Include="..\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.8.0" />
</ItemGroup>
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Rx.props" />

1
src/Avalonia.MicroCom/Avalonia.MicroCom.csproj

@ -4,6 +4,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MicroCom.Runtime" Version="0.10.5" />
<ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<ExcludeAssets>all</ExcludeAssets>

4
src/Avalonia.MicroCom/CallbackBase.cs

@ -1,4 +1,6 @@
namespace Avalonia.MicroCom
using MicroCom.Runtime;
namespace Avalonia.MicroCom
{
public abstract class CallbackBase : IUnknown, IMicroComShadowContainer
{

9
src/Avalonia.MicroCom/IMicroComExceptionCallback.cs

@ -1,9 +0,0 @@
using System;
namespace Avalonia.MicroCom
{
public interface IMicroComExceptionCallback
{
void RaiseException(Exception e);
}
}

9
src/Avalonia.MicroCom/IMicroComShadowContainer.cs

@ -1,9 +0,0 @@
namespace Avalonia.MicroCom
{
public interface IMicroComShadowContainer
{
MicroComShadow Shadow { get; set; }
void OnReferencedFromNative();
void OnUnreferencedFromNative();
}
}

8
src/Avalonia.MicroCom/IUnknown.cs

@ -1,8 +0,0 @@
using System;
namespace Avalonia.MicroCom
{
public interface IUnknown : IDisposable
{
}
}

17
src/Avalonia.MicroCom/LocalInterop.cs

@ -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;
}
}
}

110
src/Avalonia.MicroCom/MicroComProxyBase.cs

@ -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<T>() 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);
}
}
}

143
src/Avalonia.MicroCom/MicroComRuntime.cs

@ -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<Type, IntPtr> _vtables = new ConcurrentDictionary<Type, IntPtr>();
private static ConcurrentDictionary<Type, Func<IntPtr, bool, object>> _factories =
new ConcurrentDictionary<Type, Func<IntPtr, bool, object>>();
private static ConcurrentDictionary<Type, Guid> _guids = new ConcurrentDictionary<Type, Guid>();
private static ConcurrentDictionary<Guid, Type> _guidsToTypes = new ConcurrentDictionary<Guid, Type>();
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<IntPtr, bool, object> proxyFactory)
{
_factories[t] = proxyFactory;
_guids[t] = guid;
_guidsToTypes[guid] = t;
}
public static Guid GetGuidFor(Type type) => _guids[type];
public static T CreateProxyFor<T>(void* pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), new IntPtr(pObject), ownsHandle);
public static T CreateProxyFor<T>(IntPtr pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), pObject, ownsHandle);
public static T CreateProxyOrNullFor<T>(void* pObject, bool ownsHandle) where T : class =>
pObject == null ? null : (T)CreateProxyFor(typeof(T), new IntPtr(pObject), ownsHandle);
public static T CreateProxyOrNullFor<T>(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<T>(this T obj, bool owned = false) where T : IUnknown
=> new IntPtr(GetNativePointer(obj, owned));
public static void* GetNativePointer<T>(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<T>(this T iface) where T : IUnknown
{
var proxy = (MicroComProxyBase)(object)iface;
var ownedPointer = GetNativePointer(iface, true);
return CreateProxyFor<T>(ownedPointer, true);
}
public static T QueryInterface<T>(this IUnknown unknown) where T : IUnknown
{
var proxy = (MicroComProxyBase)unknown;
return proxy.QueryInterface<T>();
}
public static void UnsafeAddRef(this IUnknown unknown)
{
((MicroComProxyBase)unknown).AddRef();
}
public static void UnsafeRelease(this IUnknown unknown)
{
((MicroComProxyBase)unknown).Release();
}
}
}

177
src/Avalonia.MicroCom/MicroComShadow.cs

@ -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<Type, IntPtr> _shadows = new Dictionary<Type, IntPtr>();
private readonly Dictionary<IntPtr, Type> _backShadows = new Dictionary<IntPtr, Type>();
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<Ccw>());
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<IntPtr> toRemove = null;
foreach (var kv in _backShadows)
{
var ccw = (Ccw*)kv.Key;
if (ccw->RefCount == 0)
{
toRemove ??= new List<IntPtr>();
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;
}
}

47
src/Avalonia.MicroCom/MicroComVtblBase.cs

@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Avalonia.MicroCom
{
public unsafe class MicroComVtblBase
{
private List<IntPtr> _methods = new List<IntPtr>();
[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);
}
}

1
src/Avalonia.Native/Avalonia.Native.csproj

@ -6,7 +6,6 @@
<IsPackable Condition="'$([MSBuild]::IsOSPlatform(OSX))' == 'True'">true</IsPackable>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MicroComGeneratorRuntimeNamespace>Avalonia.MicroCom</MicroComGeneratorRuntimeNamespace>
</PropertyGroup>
<ItemGroup Condition="'$(PackAvaloniaNative)' == 'true'">

1
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
{

1
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
{

1
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
{

4
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;

2
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);

4
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

28
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<Screen> 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;
}
}

8
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();

1
src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj

@ -5,7 +5,6 @@
<IsPackable>true</IsPackable>
<PackageId>Avalonia.Markup.Xaml.Loader</PackageId>
<DefineConstants>$(DefineConstants);XAMLX_INTERNAL</DefineConstants>
<LangVersion>11</LangVersion>
</PropertyGroup>
<!--Disable Net Perf. analyzer for submodule to avoid commit issue -->
<PropertyGroup>

1
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<RootNamespace>Avalonia</RootNamespace>
<LangVersion>11</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Remove="Markup\Parsers\Nodes\ExpressionGrammer" />

4
src/Web/Avalonia.Web/Avalonia.Web.props

@ -1,5 +1,5 @@
<Project>
<Project>
<PropertyGroup>
<EmccExtraLDFlags>$(EmccExtraLDFlags) --js-library="$(MSBuildThisFileDirectory)\interop.js"</EmccExtraLDFlags>
<EmccInitialHeapSize>16384000</EmccInitialHeapSize> <!-- must be a multiple of 64KiB, 1024000 * num MB, number grows -->
</PropertyGroup>
</Project>

30
src/Web/Avalonia.Web/Avalonia.Web.targets

@ -4,4 +4,34 @@
<NativeFileReference Include="$(HarfBuzzSharpStaticLibraryPath)\3.1.7\libHarfBuzzSharp.a" />
<NativeFileReference Include="$(SkiaSharpStaticLibraryPath)\3.1.7\libSkiaSharp.a" />
</ItemGroup>
<PropertyGroup>
<UseAvaloniaWasmDefaultOptimizations Condition="'$(UseAvaloniaWasmDefaultOptimizations)'==''">True</UseAvaloniaWasmDefaultOptimizations>
<EmccExtraLDFlags>$(EmccExtraLDFlags) --js-library="$(MSBuildThisFileDirectory)\interop.js"</EmccExtraLDFlags>
<EmccFlags>$(EmccExtraLDFlags) -sERROR_ON_UNDEFINED_SYMBOLS=0</EmccFlags>
<WasmBuildNative>true</WasmBuildNative>
</PropertyGroup>
<PropertyGroup Condition="'$(UseAvaloniaWasmDefaultOptimizations)'=='True'">
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<InvariantGlobalization>true</InvariantGlobalization>
<EmccCompileOptimizationFlag>-Oz</EmccCompileOptimizationFlag>
<EmccLinkOptimizationFlag>-Oz</EmccLinkOptimizationFlag>
<WasmEmitSymbolMap>false</WasmEmitSymbolMap>
<WasmNativeDebugSymbols>false</WasmNativeDebugSymbols>
<WasmDebugLevel>0</WasmDebugLevel>
<WasmEnableExceptionHandling>false</WasmEnableExceptionHandling>
<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
<DebuggerSupport>false</DebuggerSupport>
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
<EventSourceSupport>false</EventSourceSupport>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<UseNativeHttpHandler>true</UseNativeHttpHandler>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<IncludeSatelliteDllsProjectOutputGroup>false</IncludeSatelliteDllsProjectOutputGroup>
</PropertyGroup>
</Project>

14
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)

2
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(

2
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",

35
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<string, HTMLElement>;
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 {

6
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";

26
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) {

1
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -3,7 +3,6 @@
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageId>Avalonia.Win32</PackageId>
<MicroComGeneratorRuntimeNamespace>Avalonia.MicroCom</MicroComGeneratorRuntimeNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />

7
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<Win32Com.IDataObject>(wrapper);
var ptr = wrapper.GetNativeIntPtr<Win32Com.IDataObject>();
var hr = UnmanagedMethods.OleSetClipboard(ptr);
if (hr == 0)
@ -107,7 +108,7 @@ namespace Avalonia.Win32
if (hr == 0)
{
using var proxy = MicroCom.MicroComRuntime.CreateProxyFor<Win32Com.IDataObject>(dataObject, true);
using var proxy = MicroComRuntime.CreateProxyFor<Win32Com.IDataObject>(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<Win32Com.IDataObject>(dataObject, true);
using var proxy = MicroComRuntime.CreateProxyFor<Win32Com.IDataObject>(dataObject, true);
using var wrapper = new OleDataObject(proxy);
var rv = wrapper.Get(format);
return rv;

5
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<Win32Com.IDataObject>(dataObject);
var srcPtr = MicroCom.MicroComRuntime.GetNativeIntPtr<Win32Com.IDropSource>(src);
var objPtr = MicroComRuntime.GetNativeIntPtr<Win32Com.IDataObject>(dataObject);
var srcPtr = MicroComRuntime.GetNativeIntPtr<Win32Com.IDropSource>(src);
UnmanagedMethods.DoDragDrop(objPtr, srcPtr, (int)allowed, out var finalEffect);

6
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;

3
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;
}

2
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

1
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

8
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;
}
}

1
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
{

1
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
{

1
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
{

1
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
{

1
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
{

1
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
{

1
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
{

3
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;
}

2
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;

21
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)

Loading…
Cancel
Save