Selaa lähdekoodia

Merge branch 'fixes/textProcessingBugs' of https://github.com/Gillibald/Avalonia into fixes/textProcessingBugs

Benedikt Stebner 3 vuotta sitten
vanhempi
sitoutus
b36804fc5a
100 muutettua tiedostoa jossa 3552 lisäystä ja 3086 poistoa
  1. 1 1
      Avalonia.sln
  2. 0 2
      build/CoreLibraries.props
  3. 1 1
      build/ImageSharp.props
  4. 5 0
      native/Avalonia.Native/inc/rendertarget.h
  5. 17 0
      native/Avalonia.Native/src/OSX/AutoFitContentView.h
  6. 106 0
      native/Avalonia.Native/src/OSX/AutoFitContentView.mm
  7. 72 6
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  8. 5 1
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
  9. 11 0
      native/Avalonia.Native/src/OSX/AvnPanelWindow.mm
  10. 27 0
      native/Avalonia.Native/src/OSX/AvnView.h
  11. 712 0
      native/Avalonia.Native/src/OSX/AvnView.mm
  12. 441 0
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  13. 17 0
      native/Avalonia.Native/src/OSX/INSWindowHolder.h
  14. 18 0
      native/Avalonia.Native/src/OSX/IWindowStateChanged.h
  15. 9 0
      native/Avalonia.Native/src/OSX/PopupImpl.h
  16. 68 0
      native/Avalonia.Native/src/OSX/PopupImpl.mm
  17. 24 0
      native/Avalonia.Native/src/OSX/ResizeScope.h
  18. 18 0
      native/Avalonia.Native/src/OSX/ResizeScope.mm
  19. 1 1
      native/Avalonia.Native/src/OSX/SystemDialogs.mm
  20. 130 0
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  21. 589 0
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  22. 96 0
      native/Avalonia.Native/src/OSX/WindowImpl.h
  23. 552 0
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  24. 17 0
      native/Avalonia.Native/src/OSX/WindowInterfaces.h
  25. 25 0
      native/Avalonia.Native/src/OSX/WindowProtocol.h
  26. 0 12
      native/Avalonia.Native/src/OSX/app.mm
  27. 2 2
      native/Avalonia.Native/src/OSX/automation.h
  28. 2 1
      native/Avalonia.Native/src/OSX/automation.mm
  29. 1 2
      native/Avalonia.Native/src/OSX/common.h
  30. 0 1
      native/Avalonia.Native/src/OSX/cursor.mm
  31. 1 6
      native/Avalonia.Native/src/OSX/main.mm
  32. 0 1
      native/Avalonia.Native/src/OSX/menu.h
  33. 2 4
      native/Avalonia.Native/src/OSX/menu.mm
  34. 0 4
      native/Avalonia.Native/src/OSX/rendertarget.mm
  35. 0 77
      native/Avalonia.Native/src/OSX/window.h
  36. 0 2590
      native/Avalonia.Native/src/OSX/window.mm
  37. 1 0
      samples/BindingDemo/BindingDemo.csproj
  38. 0 1
      samples/ControlCatalog.NetCore/Program.cs
  39. 2 0
      samples/ControlCatalog/ControlCatalog.csproj
  40. 10 0
      samples/ControlCatalog/Pages/ComboBoxPage.xaml
  41. 6 0
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  42. 1 1
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  43. 2 1
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  44. 1 0
      samples/PlatformSanityChecks/PlatformSanityChecks.csproj
  45. 3 0
      samples/Previewer/Previewer.csproj
  46. 1 0
      samples/RenderDemo/RenderDemo.csproj
  47. 1 0
      samples/Sandbox/Sandbox.csproj
  48. 1 0
      samples/VirtualizationDemo/VirtualizationDemo.csproj
  49. 1 0
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  50. 1 0
      samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
  51. 6 0
      src/Avalonia.Base/Collections/AvaloniaList.cs
  52. 12 7
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  53. 2 3
      src/Avalonia.Base/DirectPropertyBase.cs
  54. 9 9
      src/Avalonia.Base/Input/InputElement.cs
  55. 1 1
      src/Avalonia.Base/Layout/UniformGridLayout.cs
  56. 0 51
      src/Avalonia.Base/Logging/ILogSink.cs
  57. 3 35
      src/Avalonia.Base/Logging/TraceLogSink.cs
  58. 2 2
      src/Avalonia.Base/Media/ConicGradientBrush.cs
  59. 3 5
      src/Avalonia.Base/StyledPropertyBase.cs
  60. 5 0
      src/Avalonia.Base/Styling/IStyleInstance.cs
  61. 6 4
      src/Avalonia.Base/Styling/PropertySetterInstance.cs
  62. 15 16
      src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs
  63. 0 9
      src/Avalonia.Base/Styling/Setter.cs
  64. 18 17
      src/Avalonia.Base/Styling/StyleInstance.cs
  65. 1 2
      src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs
  66. 2 0
      src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs
  67. 21 12
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  68. 43 32
      src/Avalonia.Controls/Canvas.cs
  69. 1 1
      src/Avalonia.Controls/ContextMenu.cs
  70. 2 2
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  71. 1 1
      src/Avalonia.Controls/DockPanel.cs
  72. 1 1
      src/Avalonia.Controls/MaskedTextBox.cs
  73. 11 11
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  74. 23 8
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  75. 1 1
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  76. 44 12
      src/Avalonia.Controls/Primitives/IPopupHost.cs
  77. 19 23
      src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
  78. 135 17
      src/Avalonia.Controls/Primitives/Popup.cs
  79. 15 24
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  80. 3 3
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  81. 1 1
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  82. 3 3
      src/Avalonia.Controls/Primitives/Track.cs
  83. 2 2
      src/Avalonia.Controls/Slider.cs
  84. 1 1
      src/Avalonia.Controls/SplitView.cs
  85. 23 8
      src/Avalonia.Controls/TextBox.cs
  86. 48 5
      src/Avalonia.Controls/Viewbox.cs
  87. 2 2
      src/Avalonia.Controls/Window.cs
  88. 0 1
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  89. 2 1
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
  90. 10 3
      src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs
  91. 3 3
      src/Avalonia.FreeDesktop/NativeMethods.cs
  92. 7 0
      src/Avalonia.Native/WindowImpl.cs
  93. 1 0
      src/Avalonia.Native/avn.idl
  94. 10 8
      src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml
  95. 12 10
      src/Avalonia.Themes.Default/Controls/PopupRoot.xaml
  96. 1 0
      src/Avalonia.Themes.Default/Controls/TextBox.xaml
  97. 2 1
      src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml
  98. 9 7
      src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml
  99. 8 6
      src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml
  100. 1 0
      src/Avalonia.Themes.Fluent/Controls/TextBox.xaml

+ 1 - 1
Avalonia.sln

@@ -99,7 +99,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
 		build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
 		build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
 		build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
-		build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
 		build\Microsoft.CSharp.props = build\Microsoft.CSharp.props
 		build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
 		build\Moq.props = build\Moq.props
@@ -118,6 +117,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
 		build\System.Memory.props = build\System.Memory.props
 		build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
 		build\XUnit.props = build\XUnit.props
+		build\ImageSharp.props = build\ImageSharp.props
 	EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"

+ 0 - 2
build/CoreLibraries.props

@@ -3,8 +3,6 @@
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Base/Avalonia.Base.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Controls/Avalonia.Controls.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj" />
-      <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj" />
-      <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.OpenGL/Avalonia.OpenGL.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Dialogs/Avalonia.Dialogs.csproj" />
       <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup/Avalonia.Markup.csproj" />

+ 1 - 1
build/Magick.NET-Q16-AnyCPU.props → build/ImageSharp.props

@@ -1,5 +1,5 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.9.0.2" />
+    <PackageReference Include="SixLabors.ImageSharp" Version="2.1.1" />
   </ItemGroup>
 </Project>

+ 5 - 0
native/Avalonia.Native/inc/rendertarget.h

@@ -1,3 +1,8 @@
+#pragma once
+
+#include "com.h"
+#include "comimpl.h"
+#include "avalonia-native.h"
 
 @protocol IRenderTarget
 -(void) setNewLayer: (CALayer*) layer;

+ 17 - 0
native/Avalonia.Native/src/OSX/AutoFitContentView.h

@@ -0,0 +1,17 @@
+//
+// Created by Dan Walmsley on 05/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#pragma once
+
+#import <Foundation/Foundation.h>
+#include "avalonia-native.h"
+
+@interface AutoFitContentView : NSView
+-(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content;
+-(void) ShowTitleBar: (bool) show;
+-(void) SetTitleBarHeightHint: (double) height;
+
+-(void) ShowBlur: (bool) show;
+@end

+ 106 - 0
native/Avalonia.Native/src/OSX/AutoFitContentView.mm

@@ -0,0 +1,106 @@
+//
+// Created by Dan Walmsley on 05/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#include "AvnView.h"
+#include "AutoFitContentView.h"
+#include "WindowInterfaces.h"
+#include "WindowProtocol.h"
+
+@implementation AutoFitContentView
+{
+    NSVisualEffectView* _titleBarMaterial;
+    NSBox* _titleBarUnderline;
+    NSView* _content;
+    NSVisualEffectView* _blurBehind;
+    double _titleBarHeightHint;
+    bool _settingSize;
+}
+
+-(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content
+{
+    _titleBarHeightHint = -1;
+    _content = content;
+    _settingSize = false;
+
+    [self setAutoresizesSubviews:true];
+    [self setWantsLayer:true];
+
+    _titleBarMaterial = [NSVisualEffectView new];
+    [_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow];
+    [_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar];
+    [_titleBarMaterial setWantsLayer:true];
+    _titleBarMaterial.hidden = true;
+
+    _titleBarUnderline = [NSBox new];
+    _titleBarUnderline.boxType = NSBoxSeparator;
+    _titleBarUnderline.fillColor = [NSColor underPageBackgroundColor];
+    _titleBarUnderline.hidden = true;
+
+    [self addSubview:_titleBarMaterial];
+    [self addSubview:_titleBarUnderline];
+
+    _blurBehind = [NSVisualEffectView new];
+    [_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
+    [_blurBehind setMaterial:NSVisualEffectMaterialLight];
+    [_blurBehind setWantsLayer:true];
+    _blurBehind.hidden = true;
+
+    [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+    [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
+    [self addSubview:_blurBehind];
+    [self addSubview:_content];
+
+    [self setWantsLayer:true];
+    return self;
+}
+
+-(void) ShowBlur:(bool)show
+{
+    _blurBehind.hidden = !show;
+}
+
+-(void) ShowTitleBar: (bool) show
+{
+    _titleBarMaterial.hidden = !show;
+    _titleBarUnderline.hidden = !show;
+}
+
+-(void) SetTitleBarHeightHint: (double) height
+{
+    _titleBarHeightHint = height;
+
+    [self setFrameSize:self.frame.size];
+}
+
+-(void)setFrameSize:(NSSize)newSize
+{
+    if(_settingSize)
+    {
+        return;
+    }
+
+    _settingSize = true;
+    [super setFrameSize:newSize];
+
+    auto window = static_cast<id <AvnWindowProtocol>>([self window]);
+
+    // TODO get actual titlebar size
+
+    double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint;
+
+    NSRect tbar;
+    tbar.origin.x = 0;
+    tbar.origin.y = newSize.height - height;
+    tbar.size.width = newSize.width;
+    tbar.size.height = height;
+
+    [_titleBarMaterial setFrame:tbar];
+    tbar.size.height = height < 1 ? 0 : 1;
+    [_titleBarUnderline setFrame:tbar];
+
+    _settingSize = false;
+}
+@end

+ 72 - 6
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@@ -7,6 +7,24 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; };
+		1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; };
+		183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DB45C7D892E61BF388C /* WindowInterfaces.h */; };
+		1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */; };
+		183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; };
+		1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; };
+		1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; };
+		183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; };
+		18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; };
+		18391AC16726CBC45856233B /* AvnWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839155B28B20FFB672D29C6 /* AvnWindow.mm */; };
+		18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183910513F396141938832B5 /* PopupImpl.h */; };
+		18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; };
+		18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; };
+		18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; };
+		18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391BB698579F40F1783F31 /* PopupImpl.mm */; };
+		18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; };
+		18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; };
+		18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839122E037567BDD1D09DEB /* WindowProtocol.h */; };
 		1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; };
 		1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; };
 		1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; };
@@ -28,13 +46,30 @@
 		AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
 		AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; };
 		AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
-		AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
 		AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
 		BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; };
 		BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		183910513F396141938832B5 /* PopupImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupImpl.h; sourceTree = "<group>"; };
+		1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = "<group>"; };
+		1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = "<group>"; };
+		183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = "<group>"; };
+		1839155B28B20FFB672D29C6 /* AvnWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnWindow.mm; sourceTree = "<group>"; };
+		183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = "<group>"; };
+		18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFitContentView.h; sourceTree = "<group>"; };
+		1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = "<group>"; };
+		18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = "<group>"; };
+		1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = "<group>"; };
+		18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = "<group>"; };
+		183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = "<group>"; };
+		18391BB698579F40F1783F31 /* PopupImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PopupImpl.mm; sourceTree = "<group>"; };
+		18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = "<group>"; };
+		18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = "<group>"; };
+		18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = "<group>"; };
+		18391DB45C7D892E61BF388C /* WindowInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowInterfaces.h; sourceTree = "<group>"; };
+		18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = "<group>"; };
 		1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = "<group>"; };
 		1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = "<group>"; };
 		1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = "<group>"; };
@@ -48,7 +83,6 @@
 		37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; };
 		37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = "<group>"; };
 		37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = "<group>"; };
-		37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = "<group>"; };
 		37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = "<group>"; };
 		37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = "<group>"; };
 		37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; };
@@ -62,7 +96,6 @@
 		AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
 		AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
 		AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
-		AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = "<group>"; };
 		AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
 		AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = "<group>"; };
@@ -118,8 +151,6 @@
 				AB661C212148288600291242 /* common.h */,
 				379860FE214DA0C000CD0246 /* KeyTransform.h */,
 				37E2330E21583241000CB7E2 /* KeyTransform.mm */,
-				AB661C1F2148286E00291242 /* window.mm */,
-				37C09D8A21581EF2006A6758 /* window.h */,
 				AB00E4F62147CA920032A60A /* main.mm */,
 				37155CE3233C00EB0034DCE9 /* menu.h */,
 				520624B222973F4100C4DCEF /* menu.mm */,
@@ -130,6 +161,24 @@
 				37C09D8721580FE4006A6758 /* SystemDialogs.mm */,
 				AB7A61F02147C815003C5833 /* Products */,
 				AB661C1C2148230E00291242 /* Frameworks */,
+				18391676ECF0E983F4964357 /* WindowBaseImpl.mm */,
+				183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */,
+				18391BBB7782C296D424071F /* INSWindowHolder.h */,
+				183919BF108EB72A029F7671 /* WindowImpl.mm */,
+				18391CD090AA776E7E841AC9 /* WindowImpl.h */,
+				183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */,
+				18391E45702740FE9DD69695 /* ResizeScope.mm */,
+				1839171D898F9BFC1373631A /* ResizeScope.h */,
+				1839132D0E2454D911F1D1F9 /* AvnView.mm */,
+				18391D1669284AD2EC9E866A /* AvnView.h */,
+				1839166350F32661F3ABD70F /* AutoFitContentView.mm */,
+				18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */,
+				18391884C7476DA4E53A492D /* AvnPanelWindow.mm */,
+				1839122E037567BDD1D09DEB /* WindowProtocol.h */,
+				1839155B28B20FFB672D29C6 /* AvnWindow.mm */,
+				18391DB45C7D892E61BF388C /* WindowInterfaces.h */,
+				18391BB698579F40F1783F31 /* PopupImpl.mm */,
+				183910513F396141938832B5 /* PopupImpl.h */,
 			);
 			sourceTree = "<group>";
 		};
@@ -150,6 +199,16 @@
 			files = (
 				37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
 				BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */,
+				183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */,
+				1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */,
+				183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */,
+				18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */,
+				18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */,
+				18391ED5F611FF62C45F196D /* AvnView.h in Headers */,
+				18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */,
+				18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */,
+				183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */,
+				18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -228,7 +287,14 @@
 				1A465D10246AB61600C5858B /* dnd.mm in Sources */,
 				AB00E4F72147CA920032A60A /* main.mm in Sources */,
 				37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
-				AB661C202148286E00291242 /* window.mm in Sources */,
+				1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */,
+				1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */,
+				18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */,
+				18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */,
+				18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */,
+				1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */,
+				18391AC16726CBC45856233B /* AvnWindow.mm in Sources */,
+				18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 5 - 1
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme

@@ -56,10 +56,14 @@
       </MacroExpansion>
       <CommandLineArguments>
          <CommandLineArgument
-            argument = "bin/Debug/netcoreapp3.1/ControlCatalog.NetCore.dll"
+            argument = "bin/Debug/net6.0/ControlCatalog.NetCore.dll"
             isEnabled = "YES">
          </CommandLineArgument>
       </CommandLineArguments>
+      <LocationScenarioReference
+         identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
+         referenceType = "1">
+      </LocationScenarioReference>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"

+ 11 - 0
native/Avalonia.Native/src/OSX/AvnPanelWindow.mm

@@ -0,0 +1,11 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#pragma once
+
+#define IS_NSPANEL
+
+#include "AvnWindow.mm"
+

+ 27 - 0
native/Avalonia.Native/src/OSX/AvnView.h

@@ -0,0 +1,27 @@
+//
+// Created by Dan Walmsley on 05/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+#pragma once
+#import <Foundation/Foundation.h>
+
+
+#import <Foundation/Foundation.h>
+#import <AppKit/AppKit.h>
+#include "common.h"
+#include "WindowImpl.h"
+#include "KeyTransform.h"
+
+@class AvnAccessibilityElement;
+
+@interface AvnView : NSView<NSTextInputClient, NSDraggingDestination>
+-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
+-(NSEvent* _Nonnull) lastMouseDownEvent;
+-(AvnPoint) translateLocalPoint:(AvnPoint)pt;
+-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose;
+-(void) onClosed;
+
+-(AvnPlatformResizeReason) getResizeReason;
+-(void) setResizeReason:(AvnPlatformResizeReason)reason;
++ (AvnPoint)toAvnPoint:(CGPoint)p;
+@end

+ 712 - 0
native/Avalonia.Native/src/OSX/AvnView.mm

@@ -0,0 +1,712 @@
+//
+// Created by Dan Walmsley on 05/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#include "AvnView.h"
+#include "automation.h"
+#import "WindowInterfaces.h"
+
+@implementation AvnView
+{
+    ComPtr<WindowBaseImpl> _parent;
+    NSTrackingArea* _area;
+    bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed;
+    AvnInputModifiers _modifierState;
+    NSEvent* _lastMouseDownEvent;
+    bool _lastKeyHandled;
+    AvnPixelSize _lastPixelSize;
+    NSObject<IRenderTarget>* _renderTarget;
+    AvnPlatformResizeReason _resizeReason;
+    AvnAccessibilityElement* _accessibilityChild;
+}
+
+- (void)onClosed
+{
+    @synchronized (self)
+    {
+        _parent = nullptr;
+    }
+}
+
+- (NSEvent*) lastMouseDownEvent
+{
+    return _lastMouseDownEvent;
+}
+
+- (void) updateRenderTarget
+{
+    [_renderTarget resize:_lastPixelSize withScale:static_cast<float>([[self window] backingScaleFactor])];
+    [self setNeedsDisplayInRect:[self frame]];
+}
+
+-(AvnView*)  initWithParent: (WindowBaseImpl*) parent
+{
+    self = [super init];
+    _renderTarget = parent->renderTarget;
+    [self setWantsLayer:YES];
+    [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize];
+
+    _parent = parent;
+    _area = nullptr;
+    _lastPixelSize.Height = 100;
+    _lastPixelSize.Width = 100;
+    [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]];
+
+    _modifierState = AvnInputModifiersNone;
+    return self;
+}
+
+- (BOOL)isFlipped
+{
+    return YES;
+}
+
+- (BOOL)wantsUpdateLayer
+{
+    return YES;
+}
+
+- (void)setLayer:(CALayer *)layer
+{
+    [_renderTarget setNewLayer: layer];
+    [super setLayer: layer];
+}
+
+- (BOOL)isOpaque
+{
+    return YES;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+    return true;
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)event
+{
+    return true;
+}
+
+- (BOOL)canBecomeKeyView
+{
+    return true;
+}
+
+-(void)setFrameSize:(NSSize)newSize
+{
+    [super setFrameSize:newSize];
+
+    if(_area != nullptr)
+    {
+        [self removeTrackingArea:_area];
+        _area = nullptr;
+    }
+
+    if (_parent == nullptr)
+    {
+        return;
+    }
+
+    NSRect rect = NSZeroRect;
+    rect.size = newSize;
+
+    NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingEnabledDuringMouseDrag;
+    _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr];
+    [self addTrackingArea:_area];
+
+    _parent->UpdateCursor();
+
+    auto fsize = [self convertSizeToBacking: [self frame].size];
+
+    if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height)
+    {
+        _lastPixelSize.Width = (int)fsize.width;
+        _lastPixelSize.Height = (int)fsize.height;
+        [self updateRenderTarget];
+
+        auto reason = [self inLiveResize] ? ResizeUser : _resizeReason;
+        _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason);
+    }
+}
+
+- (void)updateLayer
+{
+    AvnInsidePotentialDeadlock deadlock;
+    if (_parent == nullptr)
+    {
+        return;
+    }
+
+    _parent->BaseEvents->RunRenderPriorityJobs();
+
+    if (_parent == nullptr)
+    {
+        return;
+    }
+
+    _parent->BaseEvents->Paint();
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+    return;
+}
+
+-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose
+{
+    @autoreleasepool {
+        [_renderTarget setSwFrame:fb];
+        dispose->Release();
+    }
+}
+
+- (AvnPoint) translateLocalPoint:(AvnPoint)pt
+{
+    pt.Y = [self bounds].size.height - pt.Y;
+    return pt;
+}
+
++ (AvnPoint)toAvnPoint:(CGPoint)p
+{
+    AvnPoint result;
+
+    result.X = p.x;
+    result.Y = p.y;
+
+    return result;
+}
+
+- (void) viewDidChangeBackingProperties
+{
+    auto fsize = [self convertSizeToBacking: [self frame].size];
+    _lastPixelSize.Width = (int)fsize.width;
+    _lastPixelSize.Height = (int)fsize.height;
+    [self updateRenderTarget];
+
+    if(_parent != nullptr)
+    {
+        _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
+    }
+
+    [super viewDidChangeBackingProperties];
+}
+
+- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled
+{
+    if(_parent == nullptr)
+    {
+        return TRUE;
+    }
+
+    auto parentWindow = _parent->GetWindowProtocol();
+
+    if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
+    {
+        if(trigerInputWhenDisabled)
+        {
+            auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
+
+            if(window != nullptr)
+            {
+                window->WindowEvents->GotInputWhenDisabled();
+            }
+        }
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
+{
+    bool triggerInputWhenDisabled = type != Move;
+
+    if([self ignoreUserInput: triggerInputWhenDisabled])
+    {
+        return;
+    }
+
+    auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
+    auto avnPoint = [AvnView toAvnPoint:localPoint];
+    auto point = [self translateLocalPoint:avnPoint];
+    AvnVector delta = { 0, 0};
+
+    if(type == Wheel)
+    {
+        auto speed = 5;
+
+        if([event hasPreciseScrollingDeltas])
+        {
+            speed = 50;
+        }
+
+        delta.X = [event scrollingDeltaX] / speed;
+        delta.Y = [event scrollingDeltaY] / speed;
+
+        if(delta.X == 0 && delta.Y == 0)
+        {
+            return;
+        }
+    }
+    else if (type == Magnify)
+    {
+        delta.X = delta.Y = [event magnification];
+    }
+    else if (type == Rotate)
+    {
+        delta.X = delta.Y = [event rotation];
+    }
+    else if (type == Swipe)
+    {
+        delta.X = [event deltaX];
+        delta.Y = [event deltaY];
+    }
+
+    uint32 timestamp = static_cast<uint32>([event timestamp] * 1000);
+    auto modifiers = [self getModifiers:[event modifierFlags]];
+
+    if(type != Move ||
+            (
+                    [self window] != nil &&
+                            (
+                                    [[self window] firstResponder] == nil
+                                            || ![[[self window] firstResponder] isKindOfClass: [NSView class]]
+                            )
+            )
+            )
+        [self becomeFirstResponder];
+
+    if(_parent != nullptr)
+    {
+        _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
+    }
+
+    [super mouseMoved:event];
+}
+
+- (BOOL) resignFirstResponder
+{
+    _parent->BaseEvents->LostFocus();
+    return YES;
+}
+
+- (void)mouseMoved:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Move];
+}
+
+- (void)mouseDown:(NSEvent *)event
+{
+    _isLeftPressed = true;
+    _lastMouseDownEvent = event;
+    [self mouseEvent:event withType:LeftButtonDown];
+}
+
+- (void)otherMouseDown:(NSEvent *)event
+{
+    _lastMouseDownEvent = event;
+
+    switch(event.buttonNumber)
+    {
+        case 2:
+        case 3:
+            _isMiddlePressed = true;
+            [self mouseEvent:event withType:MiddleButtonDown];
+            break;
+        case 4:
+            _isXButton1Pressed = true;
+            [self mouseEvent:event withType:XButton1Down];
+            break;
+        case 5:
+            _isXButton2Pressed = true;
+            [self mouseEvent:event withType:XButton2Down];
+            break;
+
+        default:
+            break;
+    }
+}
+
+- (void)rightMouseDown:(NSEvent *)event
+{
+    _isRightPressed = true;
+    _lastMouseDownEvent = event;
+    [self mouseEvent:event withType:RightButtonDown];
+}
+
+- (void)mouseUp:(NSEvent *)event
+{
+    _isLeftPressed = false;
+    [self mouseEvent:event withType:LeftButtonUp];
+}
+
+- (void)otherMouseUp:(NSEvent *)event
+{
+    switch(event.buttonNumber)
+    {
+        case 2:
+        case 3:
+            _isMiddlePressed = false;
+            [self mouseEvent:event withType:MiddleButtonUp];
+            break;
+        case 4:
+            _isXButton1Pressed = false;
+            [self mouseEvent:event withType:XButton1Up];
+            break;
+        case 5:
+            _isXButton2Pressed = false;
+            [self mouseEvent:event withType:XButton2Up];
+            break;
+
+        default:
+            break;
+    }
+}
+
+- (void)rightMouseUp:(NSEvent *)event
+{
+    _isRightPressed = false;
+    [self mouseEvent:event withType:RightButtonUp];
+}
+
+- (void)mouseDragged:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Move];
+    [super mouseDragged:event];
+}
+
+- (void)otherMouseDragged:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Move];
+    [super otherMouseDragged:event];
+}
+
+- (void)rightMouseDragged:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Move];
+    [super rightMouseDragged:event];
+}
+
+- (void)scrollWheel:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Wheel];
+    [super scrollWheel:event];
+}
+
+- (void)magnifyWithEvent:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Magnify];
+    [super magnifyWithEvent:event];
+}
+
+- (void)rotateWithEvent:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Rotate];
+    [super rotateWithEvent:event];
+}
+
+- (void)swipeWithEvent:(NSEvent *)event
+{
+    [self mouseEvent:event withType:Swipe];
+    [super swipeWithEvent:event];
+}
+
+- (void)mouseEntered:(NSEvent *)event
+{
+    [super mouseEntered:event];
+}
+
+- (void)mouseExited:(NSEvent *)event
+{
+    [self mouseEvent:event withType:LeaveWindow];
+    [super mouseExited:event];
+}
+
+- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
+{
+    if([self ignoreUserInput: false])
+    {
+        return;
+    }
+
+    auto key = s_KeyMap[[event keyCode]];
+
+    uint32_t timestamp = static_cast<uint32_t>([event timestamp] * 1000);
+    auto modifiers = [self getModifiers:[event modifierFlags]];
+
+    if(_parent != nullptr)
+    {
+        _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
+    }
+}
+
+- (BOOL)performKeyEquivalent:(NSEvent *)event
+{
+    bool result = _lastKeyHandled;
+
+    _lastKeyHandled = false;
+
+    return result;
+}
+
+- (void)flagsChanged:(NSEvent *)event
+{
+    auto newModifierState = [self getModifiers:[event modifierFlags]];
+
+    bool isAltCurrentlyPressed = (_modifierState & Alt) == Alt;
+    bool isControlCurrentlyPressed = (_modifierState & Control) == Control;
+    bool isShiftCurrentlyPressed = (_modifierState & Shift) == Shift;
+    bool isCommandCurrentlyPressed = (_modifierState & Windows) == Windows;
+
+    bool isAltPressed = (newModifierState & Alt) == Alt;
+    bool isControlPressed = (newModifierState & Control) == Control;
+    bool isShiftPressed = (newModifierState & Shift) == Shift;
+    bool isCommandPressed = (newModifierState & Windows) == Windows;
+
+
+    if (isAltPressed && !isAltCurrentlyPressed)
+    {
+        [self keyboardEvent:event withType:KeyDown];
+    }
+    else if (isAltCurrentlyPressed && !isAltPressed)
+    {
+        [self keyboardEvent:event withType:KeyUp];
+    }
+
+    if (isControlPressed && !isControlCurrentlyPressed)
+    {
+        [self keyboardEvent:event withType:KeyDown];
+    }
+    else if (isControlCurrentlyPressed && !isControlPressed)
+    {
+        [self keyboardEvent:event withType:KeyUp];
+    }
+
+    if (isShiftPressed && !isShiftCurrentlyPressed)
+    {
+        [self keyboardEvent:event withType:KeyDown];
+    }
+    else if(isShiftCurrentlyPressed && !isShiftPressed)
+    {
+        [self keyboardEvent:event withType:KeyUp];
+    }
+
+    if(isCommandPressed && !isCommandCurrentlyPressed)
+    {
+        [self keyboardEvent:event withType:KeyDown];
+    }
+    else if(isCommandCurrentlyPressed && ! isCommandPressed)
+    {
+        [self keyboardEvent:event withType:KeyUp];
+    }
+
+    _modifierState = newModifierState;
+
+    [[self inputContext] handleEvent:event];
+    [super flagsChanged:event];
+}
+
+- (void)keyDown:(NSEvent *)event
+{
+    [self keyboardEvent:event withType:KeyDown];
+    [[self inputContext] handleEvent:event];
+    [super keyDown:event];
+}
+
+- (void)keyUp:(NSEvent *)event
+{
+    [self keyboardEvent:event withType:KeyUp];
+    [super keyUp:event];
+}
+
+- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod
+{
+    unsigned int rv = 0;
+
+    if (mod & NSEventModifierFlagControl)
+        rv |= Control;
+    if (mod & NSEventModifierFlagShift)
+        rv |= Shift;
+    if (mod & NSEventModifierFlagOption)
+        rv |= Alt;
+    if (mod & NSEventModifierFlagCommand)
+        rv |= Windows;
+
+    if (_isLeftPressed)
+        rv |= LeftMouseButton;
+    if (_isMiddlePressed)
+        rv |= MiddleMouseButton;
+    if (_isRightPressed)
+        rv |= RightMouseButton;
+    if (_isXButton1Pressed)
+        rv |= XButton1MouseButton;
+    if (_isXButton2Pressed)
+        rv |= XButton2MouseButton;
+
+    return (AvnInputModifiers)rv;
+}
+
+- (BOOL)hasMarkedText
+{
+    return _lastKeyHandled;
+}
+
+- (NSRange)markedRange
+{
+    return NSMakeRange(NSNotFound, 0);
+}
+
+- (NSRange)selectedRange
+{
+    return NSMakeRange(NSNotFound, 0);
+}
+
+- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
+{
+
+}
+
+- (void)unmarkText
+{
+
+}
+
+- (NSArray<NSString *> *)validAttributesForMarkedText
+{
+    return [NSArray new];
+}
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
+{
+    return [NSAttributedString new];
+}
+
+- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
+{
+    if(!_lastKeyHandled)
+    {
+        if(_parent != nullptr)
+        {
+            _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
+        }
+    }
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)point
+{
+    return 0;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
+{
+    CGRect result = { 0 };
+
+    return result;
+}
+
+- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id <NSDraggingInfo>)info
+{
+    auto localPoint = [self convertPoint:[info draggingLocation] toView:self];
+    auto avnPoint = [AvnView toAvnPoint:localPoint];
+    auto point = [self translateLocalPoint:avnPoint];
+    auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]];
+    NSDragOperation nsop = [info draggingSourceOperationMask];
+
+    auto effects = ConvertDragDropEffects(nsop);
+    int reffects = (int)_parent->BaseEvents
+            ->DragEvent(type, point, modifiers, effects,
+                    CreateClipboard([info draggingPasteboard], nil),
+                    GetAvnDataObjectHandleFromDraggingInfo(info));
+
+    NSDragOperation ret = static_cast<NSDragOperation>(0);
+
+    // Ensure that the managed part didn't add any new effects
+    reffects = (int)effects & reffects;
+
+    // OSX requires exactly one operation
+    if((reffects & (int)AvnDragDropEffects::Copy) != 0)
+        ret = NSDragOperationCopy;
+    else if((reffects & (int)AvnDragDropEffects::Move) != 0)
+        ret = NSDragOperationMove;
+    else if((reffects & (int)AvnDragDropEffects::Link) != 0)
+        ret = NSDragOperationLink;
+    if(ret == 0)
+        ret = NSDragOperationNone;
+    return ret;
+}
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+    return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender];
+}
+
+- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
+{
+    return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender];
+}
+
+- (void)draggingExited:(id <NSDraggingInfo>)sender
+{
+    [self triggerAvnDragEvent: AvnDragEventType::Leave info:sender];
+}
+
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
+{
+    return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone;
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+    return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone;
+}
+
+- (void)concludeDragOperation:(nullable id <NSDraggingInfo>)sender
+{
+
+}
+
+- (AvnPlatformResizeReason)getResizeReason
+{
+    return _resizeReason;
+}
+
+- (void)setResizeReason:(AvnPlatformResizeReason)reason
+{
+    _resizeReason = reason;
+}
+
+- (AvnAccessibilityElement *) accessibilityChild
+{
+    if (_accessibilityChild == nil)
+    {
+        auto peer = _parent->BaseEvents->GetAutomationPeer();
+
+        if (peer == nil)
+            return nil;
+
+        _accessibilityChild = [AvnAccessibilityElement acquire:peer];
+    }
+
+    return _accessibilityChild;
+}
+
+- (NSArray *)accessibilityChildren
+{
+    auto child = [self accessibilityChild];
+    return NSAccessibilityUnignoredChildrenForOnlyChild(child);
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+    return [[self accessibilityChild] accessibilityHitTest:point];
+}
+
+- (id)accessibilityFocusedUIElement
+{
+    return [[self accessibilityChild] accessibilityFocusedUIElement];
+}
+
+@end

+ 441 - 0
native/Avalonia.Native/src/OSX/AvnWindow.mm

@@ -0,0 +1,441 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+
+#import <AppKit/AppKit.h>
+#import "WindowProtocol.h"
+#import "WindowBaseImpl.h"
+
+#ifdef IS_NSPANEL
+#define BASE_CLASS NSPanel
+#define CLASS_NAME AvnPanel
+#else
+#define BASE_CLASS NSWindow
+#define CLASS_NAME AvnWindow
+#endif
+
+#import <AppKit/AppKit.h>
+#include "common.h"
+#include "menu.h"
+#include "automation.h"
+#include "WindowBaseImpl.h"
+#include "WindowImpl.h"
+#include "AvnView.h"
+#include "WindowInterfaces.h"
+#include "PopupImpl.h"
+
+@implementation CLASS_NAME
+{
+    ComPtr<WindowBaseImpl> _parent;
+    bool _closed;
+    bool _isEnabled;
+    bool _isExtended;
+    AvnMenu* _menu;
+}
+
+-(void) setIsExtended:(bool)value;
+{
+    _isExtended = value;
+}
+
+-(bool) isDialog
+{
+    return _parent->IsDialog();
+}
+
+-(double) getExtendedTitleBarHeight
+{
+    if(_isExtended)
+    {
+        for (id subview in self.contentView.superview.subviews)
+        {
+            if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")])
+            {
+                NSView *titlebarView = [subview subviews][0];
+
+                return (double)titlebarView.frame.size.height;
+            }
+        }
+
+        return -1;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+- (void)performClose:(id)sender
+{
+    if([[self delegate] respondsToSelector:@selector(windowShouldClose:)])
+    {
+        if(![[self delegate] windowShouldClose:self]) return;
+    }
+    else if([self respondsToSelector:@selector(windowShouldClose:)])
+    {
+        if(![self windowShouldClose:self]) return;
+    }
+
+    [self close];
+}
+
+- (void)pollModalSession:(nonnull NSModalSession)session
+{
+    auto response = [NSApp runModalSession:session];
+
+    if(response == NSModalResponseContinue)
+    {
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [self pollModalSession:session];
+        });
+    }
+    else if (!_closed)
+    {
+        [self orderOut:self];
+        [NSApp endModalSession:session];
+    }
+}
+
+-(void) showWindowMenuWithAppMenu
+{
+    if(_menu != nullptr)
+    {
+        auto appMenuItem = ::GetAppMenuItem();
+
+        if(appMenuItem != nullptr)
+        {
+            auto appMenu = [appMenuItem menu];
+
+            [appMenu removeItem:appMenuItem];
+
+            [_menu insertItem:appMenuItem atIndex:0];
+
+            [_menu setHasGlobalMenuItem:true];
+        }
+
+        [NSApp setMenu:_menu];
+    }
+    else
+    {
+        [self showAppMenuOnly];
+    }
+}
+
+-(void) showAppMenuOnly
+{
+    auto appMenuItem = ::GetAppMenuItem();
+
+    if(appMenuItem != nullptr)
+    {
+        auto appMenu = ::GetAppMenu();
+
+        auto nativeAppMenu = dynamic_cast<AvnAppMenu*>(appMenu);
+
+        [[appMenuItem menu] removeItem:appMenuItem];
+
+        if(_menu != nullptr)
+        {
+            [_menu setHasGlobalMenuItem:false];
+        }
+
+        [nativeAppMenu->GetNative() addItem:appMenuItem];
+
+        [NSApp setMenu:nativeAppMenu->GetNative()];
+    }
+}
+
+-(void) applyMenu:(AvnMenu *)menu
+{
+    if(menu == nullptr)
+    {
+        menu = [AvnMenu new];
+    }
+
+    _menu = menu;
+}
+
+-(CLASS_NAME*)  initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+{
+    // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/
+    // create nswindow with specific contentRect, otherwise we wont be able to resize the window
+    // until several ms after the window is physically on the screen.
+    self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false];
+
+    [self setReleasedWhenClosed:false];
+    _parent = parent;
+    [self setDelegate:self];
+    _closed = false;
+    _isEnabled = true;
+
+    [self backingScaleFactor];
+    [self setOpaque:NO];
+    [self setBackgroundColor: [NSColor clearColor]];
+
+    _isExtended = false;
+    return self;
+}
+
+- (BOOL)windowShouldClose:(NSWindow *)sender
+{
+    auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
+
+    if(window != nullptr)
+    {
+        return !window->WindowEvents->Closing();
+    }
+
+    return true;
+}
+
+- (void)windowDidChangeBackingProperties:(NSNotification *)notification
+{
+    [self backingScaleFactor];
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+    _closed = true;
+    if(_parent)
+    {
+        ComPtr<WindowBaseImpl> parent = _parent;
+        _parent = NULL;
+        [self restoreParentWindow];
+        parent->BaseEvents->Closed();
+        [parent->View onClosed];
+    }
+}
+
+-(BOOL)canBecomeKeyWindow
+{
+    // If the window has a child window being shown as a dialog then don't allow it to become the key window.
+    for(NSWindow* uch in [self childWindows])
+    {
+        auto ch = static_cast<id <AvnWindowProtocol>>(uch);
+        if(ch == nil)
+            continue;
+        if (ch.isDialog)
+            return false;
+    }
+
+    return true;
+}
+
+-(BOOL)canBecomeMainWindow
+{
+#ifdef IS_NSPANEL
+    return false;
+#else
+    return true;
+#endif
+}
+
+-(bool)shouldTryToHandleEvents
+{
+    return _isEnabled;
+}
+
+-(void) setEnabled:(bool)enable
+{
+    _isEnabled = enable;
+}
+
+-(void)becomeKeyWindow
+{
+    [self showWindowMenuWithAppMenu];
+
+    if(_parent != nullptr)
+    {
+        _parent->BaseEvents->Activated();
+    }
+
+    [super becomeKeyWindow];
+}
+
+-(void) restoreParentWindow;
+{
+    auto parent = [self parentWindow];
+
+    if(parent != nil)
+    {
+        [parent removeChildWindow:self];
+    }
+}
+
+- (void)windowDidMiniaturize:(NSNotification *)notification
+{
+    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
+
+    if(parent != nullptr)
+    {
+        parent->WindowStateChanged();
+    }
+}
+
+- (void)windowDidDeminiaturize:(NSNotification *)notification
+{
+    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
+
+    if(parent != nullptr)
+    {
+        parent->WindowStateChanged();
+    }
+}
+
+- (void)windowDidResize:(NSNotification *)notification
+{
+    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
+
+    if(parent != nullptr)
+    {
+        parent->WindowStateChanged();
+    }
+}
+
+- (void)windowWillExitFullScreen:(NSNotification *)notification
+{
+    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
+
+    if(parent != nullptr)
+    {
+        parent->StartStateTransition();
+    }
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification
+{
+    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
+
+    if(parent != nullptr)
+    {
+        parent->EndStateTransition();
+
+        if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized)
+        {
+            NSRect screenRect = [[self screen] visibleFrame];
+            [self setFrame:screenRect display:YES];
+        }
+
+        if(parent->WindowState() == Minimized)
+        {
+            [self miniaturize:nullptr];
+        }
+
+        parent->WindowStateChanged();
+    }
+}
+
+- (void)windowWillEnterFullScreen:(NSNotification *)notification
+{
+    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
+
+    if(parent != nullptr)
+    {
+        parent->StartStateTransition();
+    }
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification
+{
+    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
+
+    if(parent != nullptr)
+    {
+        parent->EndStateTransition();
+        parent->WindowStateChanged();
+    }
+}
+
+- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
+{
+    return true;
+}
+
+-(void)resignKeyWindow
+{
+    if(_parent)
+        _parent->BaseEvents->Deactivated();
+
+    [self showAppMenuOnly];
+
+    [super resignKeyWindow];
+}
+
+- (void)windowDidMove:(NSNotification *)notification
+{
+    AvnPoint position;
+
+    if(_parent != nullptr)
+    {
+        auto cparent = dynamic_cast<WindowImpl*>(_parent.getRaw());
+
+        if(cparent != nullptr)
+        {
+            if(cparent->WindowState() == Maximized)
+            {
+                cparent->SetWindowState(Normal);
+            }
+        }
+
+        _parent->GetPosition(&position);
+        _parent->BaseEvents->PositionChanged(position);
+    }
+}
+
+- (AvnPoint) translateLocalPoint:(AvnPoint)pt
+{
+    pt.Y = [self frame].size.height - pt.Y;
+    return pt;
+}
+
+- (void)sendEvent:(NSEvent *)event
+{
+    [super sendEvent:event];
+
+    /// This is to detect non-client clicks. This can only be done on Windows... not popups, hence the dynamic_cast.
+    if(_parent != nullptr && dynamic_cast<WindowImpl*>(_parent.getRaw()) != nullptr)
+    {
+        switch(event.type)
+        {
+            case NSEventTypeLeftMouseDown:
+            {
+                AvnView* view = _parent->View;
+                NSPoint windowPoint = [event locationInWindow];
+                NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil];
+
+                if (!NSPointInRect(viewPoint, view.bounds))
+                {
+                    auto avnPoint = [AvnView toAvnPoint:windowPoint];
+                    auto point = [self translateLocalPoint:avnPoint];
+                    AvnVector delta = { 0, 0 };
+
+                    _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint32>([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
+                }
+            }
+                break;
+
+            case NSEventTypeMouseEntered:
+            {
+                _parent->UpdateCursor();
+            }
+                break;
+
+            case NSEventTypeMouseExited:
+            {
+                [[NSCursor arrowCursor] set];
+            }
+                break;
+
+            default:
+                break;
+        }
+    }
+}
+
+- (void)disconnectParent {
+    _parent = nullptr;
+}
+
+@end
+

+ 17 - 0
native/Avalonia.Native/src/OSX/INSWindowHolder.h

@@ -0,0 +1,17 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H
+#define AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H
+
+@class AvnView;
+
+struct INSWindowHolder
+{
+    virtual NSWindow* _Nonnull GetNSWindow () = 0;
+    virtual NSView* _Nonnull GetNSView () = 0;
+};
+
+#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H

+ 18 - 0
native/Avalonia.Native/src/OSX/IWindowStateChanged.h

@@ -0,0 +1,18 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H
+#define AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H
+
+struct IWindowStateChanged
+{
+    virtual void WindowStateChanged () = 0;
+    virtual void StartStateTransition () = 0;
+    virtual void EndStateTransition () = 0;
+    virtual SystemDecorations Decorations () = 0;
+    virtual AvnWindowState WindowState () = 0;
+};
+
+#endif //AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H

+ 9 - 0
native/Avalonia.Native/src/OSX/PopupImpl.h

@@ -0,0 +1,9 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_POPUPIMPL_H
+#define AVALONIA_NATIVE_OSX_POPUPIMPL_H
+
+#endif //AVALONIA_NATIVE_OSX_POPUPIMPL_H

+ 68 - 0
native/Avalonia.Native/src/OSX/PopupImpl.mm

@@ -0,0 +1,68 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#include "WindowInterfaces.h"
+#include "AvnView.h"
+#include "WindowImpl.h"
+#include "automation.h"
+#include "menu.h"
+#include "common.h"
+#import "WindowBaseImpl.h"
+#import "WindowProtocol.h"
+#import <AppKit/AppKit.h>
+#include "PopupImpl.h"
+
+class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup
+{
+private:
+    BEGIN_INTERFACE_MAP()
+    INHERIT_INTERFACE_MAP(WindowBaseImpl)
+    INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup)
+    END_INTERFACE_MAP()
+    virtual ~PopupImpl(){}
+    ComPtr<IAvnWindowEvents> WindowEvents;
+    PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
+    {
+        WindowEvents = events;
+        [Window setLevel:NSPopUpMenuWindowLevel];
+    }
+protected:
+    virtual NSWindowStyleMask GetStyle() override
+    {
+        return NSWindowStyleMaskBorderless;
+    }
+
+    virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
+    {
+        START_COM_CALL;
+
+        @autoreleasepool
+        {
+            if (Window != nullptr)
+            {
+                [Window setContentSize:NSSize{x, y}];
+
+                [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];
+            }
+
+            return S_OK;
+        }
+    }
+public:
+    virtual bool ShouldTakeFocusOnShow() override
+    {
+        return false;
+    }
+};
+
+
+extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)
+{
+    @autoreleasepool
+    {
+        IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events, gl));
+        return ptr;
+    }
+}

+ 24 - 0
native/Avalonia.Native/src/OSX/ResizeScope.h

@@ -0,0 +1,24 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H
+#define AVALONIA_NATIVE_OSX_RESIZESCOPE_H
+
+#include "avalonia-native.h"
+
+@class AvnView;
+
+class ResizeScope
+{
+public:
+    ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason);
+
+    ~ResizeScope();
+private:
+    AvnView* _Nonnull _view;
+    AvnPlatformResizeReason _restore;
+};
+
+#endif //AVALONIA_NATIVE_OSX_RESIZESCOPE_H

+ 18 - 0
native/Avalonia.Native/src/OSX/ResizeScope.mm

@@ -0,0 +1,18 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#include "ResizeScope.h"
+#include "AvnView.h"
+
+ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) {
+    _view = view;
+    _restore = [view getResizeReason];
+    [view setResizeReason:reason];
+}
+
+ResizeScope::~ResizeScope() {
+    [_view setResizeReason:_restore];
+}

+ 1 - 1
native/Avalonia.Native/src/OSX/SystemDialogs.mm

@@ -1,5 +1,5 @@
 #include "common.h"
-#include "window.h"
+#include "INSWindowHolder.h"
 
 class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemDialogs>
 {

+ 130 - 0
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@@ -0,0 +1,130 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
+#define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
+
+#include "rendertarget.h"
+#include "INSWindowHolder.h"
+
+@class AutoFitContentView;
+@class AvnMenu;
+@protocol AvnWindowProtocol;
+
+class WindowBaseImpl : public virtual ComObject,
+                       public virtual IAvnWindowBase,
+                       public INSWindowHolder {
+private:
+    NSCursor *cursor;
+
+public:
+    FORWARD_IUNKNOWN()
+
+BEGIN_INTERFACE_MAP()
+        INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase)
+    END_INTERFACE_MAP()
+
+    virtual ~WindowBaseImpl();
+
+    AutoFitContentView *StandardContainer;
+    AvnView *View;
+    NSWindow * Window;
+    ComPtr<IAvnWindowBaseEvents> BaseEvents;
+    ComPtr<IAvnGlContext> _glContext;
+    NSObject <IRenderTarget> *renderTarget;
+    AvnPoint lastPositionSet;
+    NSSize lastSize;
+    NSSize lastMinSize;
+    NSSize lastMaxSize;
+    AvnMenu* lastMenu;
+    NSString *_lastTitle;
+
+    bool _shown;
+    bool _inResize;
+
+    WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl);
+
+    virtual HRESULT ObtainNSWindowHandle(void **ret) override;
+
+    virtual HRESULT ObtainNSWindowHandleRetained(void **ret) override;
+
+    virtual HRESULT ObtainNSViewHandle(void **ret) override;
+
+    virtual HRESULT ObtainNSViewHandleRetained(void **ret) override;
+
+    virtual NSWindow *GetNSWindow() override;
+
+    virtual NSView *GetNSView() override;
+
+    virtual HRESULT Show(bool activate, bool isDialog) override;
+
+    virtual bool ShouldTakeFocusOnShow();
+
+    virtual HRESULT Hide() override;
+
+    virtual HRESULT Activate() override;
+
+    virtual HRESULT SetTopMost(bool value) override;
+
+    virtual HRESULT Close() override;
+
+    virtual HRESULT GetClientSize(AvnSize *ret) override;
+
+    virtual HRESULT GetFrameSize(AvnSize *ret) override;
+
+    virtual HRESULT GetScaling(double *ret) override;
+
+    virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) override;
+
+    virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override;
+
+    virtual HRESULT Invalidate(__attribute__((unused)) AvnRect rect) override;
+
+    virtual HRESULT SetMainMenu(IAvnMenu *menu) override;
+
+    virtual HRESULT BeginMoveDrag() override;
+
+    virtual HRESULT BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) override;
+
+    virtual HRESULT GetPosition(AvnPoint *ret) override;
+
+    virtual HRESULT SetPosition(AvnPoint point) override;
+
+    virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override;
+
+    virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override;
+
+    virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) override;
+
+    virtual HRESULT SetCursor(IAvnCursor *cursor) override;
+
+    virtual void UpdateCursor();
+
+    virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) override;
+
+    virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override;
+
+    virtual HRESULT SetBlurEnabled(bool enable) override;
+
+    virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
+            IAvnClipboard *clipboard, IAvnDndResultCallback *cb,
+            void *sourceHandle) override;
+
+    virtual bool IsDialog();
+
+    id<AvnWindowProtocol> GetWindowProtocol ();
+
+protected:
+    virtual NSWindowStyleMask GetStyle();
+
+    void UpdateStyle();
+
+private:
+    void CreateNSWindow (bool isDialog);
+    void CleanNSWindow ();
+    void InitialiseNSWindow ();
+};
+
+#endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H

+ 589 - 0
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@@ -0,0 +1,589 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#include "common.h"
+#include "AvnView.h"
+#include "menu.h"
+#include "automation.h"
+#include "cursor.h"
+#include "ResizeScope.h"
+#include "AutoFitContentView.h"
+#import "WindowProtocol.h"
+#import "WindowInterfaces.h"
+#include "WindowBaseImpl.h"
+
+
+WindowBaseImpl::~WindowBaseImpl() {
+    View = nullptr;
+    Window = nullptr;
+}
+
+WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) {
+    _shown = false;
+    _inResize = false;
+    BaseEvents = events;
+    _glContext = gl;
+    renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl];
+    View = [[AvnView alloc] initWithParent:this];
+    StandardContainer = [[AutoFitContentView new] initWithContent:View];
+
+    lastPositionSet.X = 100;
+    lastPositionSet.Y = 100;
+    lastSize = NSSize { 100, 100 };
+    lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX};
+    lastMinSize = NSSize { 0, 0 };
+    _lastTitle = @"";
+
+    Window = nullptr;
+    lastMenu = nullptr;
+}
+
+HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
+    START_COM_CALL;
+
+    if (ret == nullptr) {
+        return E_POINTER;
+    }
+
+    *ret = (__bridge void *) View;
+
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) {
+    START_COM_CALL;
+
+    if (ret == nullptr) {
+        return E_POINTER;
+    }
+
+    *ret = (__bridge_retained void *) View;
+
+    return S_OK;
+}
+
+NSWindow *WindowBaseImpl::GetNSWindow() {
+    return Window;
+}
+
+NSView *WindowBaseImpl::GetNSView() {
+    return View;
+}
+
+HRESULT WindowBaseImpl::ObtainNSWindowHandleRetained(void **ret) {
+    START_COM_CALL;
+
+    if (ret == nullptr) {
+        return E_POINTER;
+    }
+
+    *ret = (__bridge_retained void *) Window;
+
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        CreateNSWindow(isDialog);
+        InitialiseNSWindow();
+
+        SetPosition(lastPositionSet);
+        UpdateStyle();
+
+        [Window setTitle:_lastTitle];
+
+        if (ShouldTakeFocusOnShow() && activate) {
+            [Window orderFront:Window];
+            [Window makeKeyAndOrderFront:Window];
+            [Window makeFirstResponder:View];
+            [NSApp activateIgnoringOtherApps:YES];
+        } else {
+            [Window orderFront:Window];
+        }
+
+        _shown = true;
+
+        return S_OK;
+    }
+}
+
+bool WindowBaseImpl::ShouldTakeFocusOnShow() {
+    return true;
+}
+
+HRESULT WindowBaseImpl::ObtainNSWindowHandle(void **ret) {
+    START_COM_CALL;
+
+    if (ret == nullptr) {
+        return E_POINTER;
+    }
+
+    *ret = (__bridge void *) Window;
+
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::Hide() {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (Window != nullptr) {
+            [Window orderOut:Window];
+
+            [GetWindowProtocol() restoreParentWindow];
+        }
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::Activate() {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (Window != nullptr) {
+            [Window makeKeyAndOrderFront:nil];
+            [NSApp activateIgnoringOtherApps:YES];
+        }
+    }
+
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::SetTopMost(bool value) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        [Window setLevel:value ? NSFloatingWindowLevel : NSNormalWindowLevel];
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::Close() {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (Window != nullptr) {
+            auto window = Window;
+            Window = nullptr;
+
+            try {
+                // Seems to throw sometimes on application exit.
+                [window close];
+            }
+            catch (NSException *) {}
+        }
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr)
+            return E_POINTER;
+
+        auto frame = [View frame];
+        ret->Width = frame.size.width;
+        ret->Height = frame.size.height;
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr)
+            return E_POINTER;
+
+        auto frame = [Window frame];
+        ret->Width = frame.size.width;
+        ret->Height = frame.size.height;
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::GetScaling(double *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr)
+            return E_POINTER;
+
+        if (Window == nullptr) {
+            *ret = 1;
+            return S_OK;
+        }
+
+        *ret = [Window backingScaleFactor];
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        lastMinSize = ToNSSize(minSize);
+        lastMaxSize = ToNSSize(maxSize);
+
+        if(Window != nullptr) {
+            [Window setContentMinSize:lastMinSize];
+            [Window setContentMaxSize:lastMaxSize];
+        }
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reason) {
+    if (_inResize) {
+        return S_OK;
+    }
+
+    _inResize = true;
+
+    START_COM_CALL;
+    auto resizeBlock = ResizeScope(View, reason);
+
+    @autoreleasepool {
+        auto maxSize = lastMaxSize;
+        auto minSize = lastMinSize;
+
+        if (x < minSize.width) {
+            x = minSize.width;
+        }
+
+        if (y < minSize.height) {
+            y = minSize.height;
+        }
+
+        if (x > maxSize.width) {
+            x = maxSize.width;
+        }
+
+        if (y > maxSize.height) {
+            y = maxSize.height;
+        }
+
+        @try {
+            if (!_shown) {
+                BaseEvents->Resized(AvnSize{x, y}, reason);
+            }
+
+            lastSize = NSSize {x, y};
+
+            if(Window != nullptr) {
+                [Window setContentSize:lastSize];
+            }
+        }
+        @finally {
+            _inResize = false;
+        }
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::Invalidate(__attribute__((unused)) AvnRect rect) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        [View setNeedsDisplayInRect:[View frame]];
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) {
+    START_COM_CALL;
+
+    auto nativeMenu = dynamic_cast<AvnAppMenu *>(menu);
+
+    lastMenu = nativeMenu->GetNative();
+
+    if(Window != nullptr) {
+        [GetWindowProtocol() applyMenu:lastMenu];
+
+        if ([Window isKeyWindow]) {
+            [GetWindowProtocol() showWindowMenuWithAppMenu];
+        }
+    }
+
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::BeginMoveDrag() {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        auto lastEvent = [View lastMouseDownEvent];
+
+        if (lastEvent == nullptr) {
+            return S_OK;
+        }
+
+        [Window performWindowDragWithEvent:lastEvent];
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) {
+    START_COM_CALL;
+
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr) {
+            return E_POINTER;
+        }
+
+        auto frame = [Window frame];
+
+        ret->X = frame.origin.x;
+        ret->Y = frame.origin.y + frame.size.height;
+
+        *ret = ConvertPointY(*ret);
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::SetPosition(AvnPoint point) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        lastPositionSet = point;
+        [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::PointToClient(AvnPoint point, AvnPoint *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr) {
+            return E_POINTER;
+        }
+
+        point = ConvertPointY(point);
+        NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
+        auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
+
+        *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr) {
+            return E_POINTER;
+        }
+
+        auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]);
+        NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)];
+        auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y));
+        *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowBaseImpl::ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) {
+    START_COM_CALL;
+
+    [View setSwRenderedFrame:fb dispose:dispose];
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        Cursor *avnCursor = dynamic_cast<Cursor *>(cursor);
+        this->cursor = avnCursor->GetNative();
+        UpdateCursor();
+
+        if (avnCursor->IsHidden()) {
+            [NSCursor hide];
+        } else {
+            [NSCursor unhide];
+        }
+
+        return S_OK;
+    }
+}
+
+void WindowBaseImpl::UpdateCursor() {
+    if (cursor != nil) {
+        [cursor set];
+    }
+}
+
+HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) {
+    START_COM_CALL;
+
+    if (View == NULL)
+        return E_FAIL;
+    *ppv = [renderTarget createSurfaceRenderTarget];
+    return static_cast<HRESULT>(*ppv == nil ? E_FAIL : S_OK);
+}
+
+HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) {
+    START_COM_CALL;
+
+    if (View == NULL)
+        return E_FAIL;
+    *retOut = ::CreateNativeControlHost(View);
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::SetBlurEnabled(bool enable) {
+    START_COM_CALL;
+
+    [StandardContainer ShowBlur:enable];
+
+    return S_OK;
+}
+
+HRESULT WindowBaseImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) {
+    START_COM_CALL;
+
+    auto item = TryGetPasteboardItem(clipboard);
+    [item setString:@"" forType:GetAvnCustomDataType()];
+    if (item == nil)
+        return E_INVALIDARG;
+    if (View == NULL)
+        return E_FAIL;
+
+    auto nsevent = [NSApp currentEvent];
+    auto nseventType = [nsevent type];
+
+    // If current event isn't a mouse one (probably due to malfunctioning user app)
+    // attempt to forge a new one
+    if (!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited)
+            || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) {
+        NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
+        auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
+        CGPoint cgpoint = NSPointToCGPoint(nspoint);
+        auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft);
+        nsevent = [NSEvent eventWithCGEvent:cgevent];
+        CFRelease(cgevent);
+    }
+
+    auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item];
+
+    auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments];
+    NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height};
+    [dragItem setDraggingFrame:dragItemRect contents:dragItemImage];
+
+    int op = 0;
+    int ieffects = (int) effects;
+    if ((ieffects & (int) AvnDragDropEffects::Copy) != 0)
+        op |= NSDragOperationCopy;
+    if ((ieffects & (int) AvnDragDropEffects::Link) != 0)
+        op |= NSDragOperationLink;
+    if ((ieffects & (int) AvnDragDropEffects::Move) != 0)
+        op |= NSDragOperationMove;
+    [View beginDraggingSessionWithItems:@[dragItem] event:nsevent
+                                 source:CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)];
+    return S_OK;
+}
+
+bool WindowBaseImpl::IsDialog() {
+    return false;
+}
+
+NSWindowStyleMask WindowBaseImpl::GetStyle() {
+    return NSWindowStyleMaskBorderless;
+}
+
+void WindowBaseImpl::UpdateStyle() {
+    [Window setStyleMask:GetStyle()];
+}
+
+void WindowBaseImpl::CleanNSWindow() {
+    if(Window != nullptr) {
+        [GetWindowProtocol() disconnectParent];
+        [Window close];
+        Window = nullptr;
+    }
+}
+
+void WindowBaseImpl::CreateNSWindow(bool isDialog) {
+    if (isDialog) {
+        if (![Window isKindOfClass:[AvnPanel class]]) {
+            CleanNSWindow();
+
+            Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()];
+        }
+    } else {
+        if (![Window isKindOfClass:[AvnWindow class]]) {
+            CleanNSWindow();
+
+            Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()];
+        }
+    }
+}
+
+void WindowBaseImpl::InitialiseNSWindow() {
+    if(Window != nullptr) {
+        [Window setContentView:StandardContainer];
+        [Window setStyleMask:NSWindowStyleMaskBorderless];
+        [Window setBackingType:NSBackingStoreBuffered];
+
+        [Window setContentSize:lastSize];
+        [Window setContentMinSize:lastMinSize];
+        [Window setContentMaxSize:lastMaxSize];
+
+        [Window setOpaque:false];
+
+        if (lastMenu != nullptr) {
+            [GetWindowProtocol() applyMenu:lastMenu];
+
+            if ([Window isKeyWindow]) {
+                [GetWindowProtocol() showWindowMenuWithAppMenu];
+            }
+        }
+    }
+}
+
+id <AvnWindowProtocol> WindowBaseImpl::GetWindowProtocol() {
+    if(Window == nullptr)
+    {
+        return nullptr;
+    }
+
+    return static_cast<id <AvnWindowProtocol>>(Window);
+}
+
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
+{
+    @autoreleasepool
+    {
+        IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl);
+        return ptr;
+    }
+}

+ 96 - 0
native/Avalonia.Native/src/OSX/WindowImpl.h

@@ -0,0 +1,96 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H
+#define AVALONIA_NATIVE_OSX_WINDOWIMPL_H
+
+#import "WindowBaseImpl.h"
+#include "IWindowStateChanged.h"
+
+class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
+{
+private:
+    bool _canResize;
+    bool _fullScreenActive;
+    SystemDecorations _decorations;
+    AvnWindowState _lastWindowState;
+    AvnWindowState _actualWindowState;
+    bool _inSetWindowState;
+    NSRect _preZoomSize;
+    bool _transitioningWindowState;
+    bool _isClientAreaExtended;
+    bool _isDialog;
+    AvnExtendClientAreaChromeHints _extendClientHints;
+
+    FORWARD_IUNKNOWN()
+BEGIN_INTERFACE_MAP()
+        INHERIT_INTERFACE_MAP(WindowBaseImpl)
+        INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow)
+    END_INTERFACE_MAP()
+    virtual ~WindowImpl()
+    {
+    }
+
+    ComPtr<IAvnWindowEvents> WindowEvents;
+
+    WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl);
+
+    void HideOrShowTrafficLights ();
+
+    virtual HRESULT Show (bool activate, bool isDialog) override;
+
+    virtual HRESULT SetEnabled (bool enable) override;
+
+    virtual HRESULT SetParent (IAvnWindow* parent) override;
+
+    void StartStateTransition () override ;
+
+    void EndStateTransition () override ;
+
+    SystemDecorations Decorations () override ;
+
+    AvnWindowState WindowState () override ;
+
+    void WindowStateChanged () override ;
+
+    bool UndecoratedIsMaximized ();
+
+    bool IsZoomed ();
+
+    void DoZoom();
+
+    virtual HRESULT SetCanResize(bool value) override;
+
+    virtual HRESULT SetDecorations(SystemDecorations value) override;
+
+    virtual HRESULT SetTitle (char* utf8title) override;
+
+    virtual HRESULT SetTitleBarColor(AvnColor color) override;
+
+    virtual HRESULT GetWindowState (AvnWindowState*ret) override;
+
+    virtual HRESULT TakeFocusFromChildren () override;
+
+    virtual HRESULT SetExtendClientArea (bool enable) override;
+
+    virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override;
+
+    virtual HRESULT GetExtendTitleBarHeight (double*ret) override;
+
+    virtual HRESULT SetExtendTitleBarHeight (double value) override;
+
+    void EnterFullScreenMode ();
+
+    void ExitFullScreenMode ();
+
+    virtual HRESULT SetWindowState (AvnWindowState state) override;
+
+    virtual bool IsDialog() override;
+
+protected:
+    virtual NSWindowStyleMask GetStyle() override;
+};
+
+#endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H

+ 552 - 0
native/Avalonia.Native/src/OSX/WindowImpl.mm

@@ -0,0 +1,552 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import <AppKit/AppKit.h>
+#include "AutoFitContentView.h"
+#include "AvnView.h"
+#include "automation.h"
+#include "WindowProtocol.h"
+
+WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) {
+    _isClientAreaExtended = false;
+    _extendClientHints = AvnDefaultChrome;
+    _fullScreenActive = false;
+    _canResize = true;
+    _decorations = SystemDecorationsFull;
+    _transitioningWindowState = false;
+    _inSetWindowState = false;
+    _lastWindowState = Normal;
+    _actualWindowState = Normal;
+    WindowEvents = events;
+    [Window disableCursorRects];
+    [Window setTabbingMode:NSWindowTabbingModeDisallowed];
+    [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+}
+
+void WindowImpl::HideOrShowTrafficLights() {
+    if (Window == nil) {
+        return;
+    }
+
+    for (id subview in Window.contentView.superview.subviews) {
+        if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
+            NSView *titlebarView = [subview subviews][0];
+            for (id button in titlebarView.subviews) {
+                if ([button isKindOfClass:[NSButton class]]) {
+                    if (_isClientAreaExtended) {
+                        auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
+
+                        [button setHidden:!wantsChrome];
+                    } else {
+                        [button setHidden:(_decorations != SystemDecorationsFull)];
+                    }
+
+                    [button setWantsLayer:true];
+                }
+            }
+        }
+    }
+}
+
+HRESULT WindowImpl::Show(bool activate, bool isDialog) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        _isDialog = isDialog;
+
+        bool created = Window == nullptr;
+
+        WindowBaseImpl::Show(activate, isDialog);
+
+        if(created)
+        {
+            if(_isClientAreaExtended)
+            {
+                [GetWindowProtocol() setIsExtended:true];
+                SetExtendClientArea(true);
+            }
+        }
+
+        HideOrShowTrafficLights();
+
+        return SetWindowState(_lastWindowState);
+    }
+}
+
+HRESULT WindowImpl::SetEnabled(bool enable) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        [GetWindowProtocol() setEnabled:enable];
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::SetParent(IAvnWindow *parent) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (parent == nullptr)
+            return E_POINTER;
+
+        auto cparent = dynamic_cast<WindowImpl *>(parent);
+        if (cparent == nullptr)
+            return E_INVALIDARG;
+
+        // If one tries to show a child window with a minimized parent window, then the parent window will be
+        // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
+        // state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
+        if (cparent->WindowState() == Minimized)
+            cparent->SetWindowState(Normal);
+
+        [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
+        [cparent->Window addChildWindow:Window ordered:NSWindowAbove];
+
+        UpdateStyle();
+
+        return S_OK;
+    }
+}
+
+void WindowImpl::StartStateTransition() {
+    _transitioningWindowState = true;
+}
+
+void WindowImpl::EndStateTransition() {
+    _transitioningWindowState = false;
+}
+
+SystemDecorations WindowImpl::Decorations() {
+    return _decorations;
+}
+
+AvnWindowState WindowImpl::WindowState() {
+    return _lastWindowState;
+}
+
+void WindowImpl::WindowStateChanged() {
+    if (_shown && !_inSetWindowState && !_transitioningWindowState) {
+        AvnWindowState state;
+        GetWindowState(&state);
+
+        if (_lastWindowState != state) {
+            if (_isClientAreaExtended) {
+                if (_lastWindowState == FullScreen) {
+                    // we exited fs.
+                    if (_extendClientHints & AvnOSXThickTitleBar) {
+                        Window.toolbar = [NSToolbar new];
+                        Window.toolbar.showsBaselineSeparator = false;
+                    }
+
+                    [Window setTitlebarAppearsTransparent:true];
+
+                    [StandardContainer setFrameSize:StandardContainer.frame.size];
+                } else if (state == FullScreen) {
+                    // we entered fs.
+                    if (_extendClientHints & AvnOSXThickTitleBar) {
+                        Window.toolbar = nullptr;
+                    }
+
+                    [Window setTitlebarAppearsTransparent:false];
+
+                    [StandardContainer setFrameSize:StandardContainer.frame.size];
+                }
+            }
+
+            _lastWindowState = state;
+            _actualWindowState = state;
+            WindowEvents->WindowStateChanged(state);
+        }
+    }
+}
+
+bool WindowImpl::UndecoratedIsMaximized() {
+    auto windowSize = [Window frame];
+    auto available = [Window screen].visibleFrame;
+    return CGRectEqualToRect(windowSize, available);
+}
+
+bool WindowImpl::IsZoomed() {
+    return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized();
+}
+
+void WindowImpl::DoZoom() {
+    switch (_decorations) {
+        case SystemDecorationsNone:
+        case SystemDecorationsBorderOnly:
+            [Window setFrame:[Window screen].visibleFrame display:true];
+            break;
+
+
+        case SystemDecorationsFull:
+            [Window performZoom:Window];
+            break;
+    }
+}
+
+HRESULT WindowImpl::SetCanResize(bool value) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        _canResize = value;
+        UpdateStyle();
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::SetDecorations(SystemDecorations value) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        auto currentWindowState = _lastWindowState;
+        _decorations = value;
+
+        if (_fullScreenActive) {
+            return S_OK;
+        }
+
+        UpdateStyle();
+
+        HideOrShowTrafficLights();
+
+        switch (_decorations) {
+            case SystemDecorationsNone:
+                [Window setHasShadow:NO];
+                [Window setTitleVisibility:NSWindowTitleHidden];
+                [Window setTitlebarAppearsTransparent:YES];
+
+                if (currentWindowState == Maximized) {
+                    if (!UndecoratedIsMaximized()) {
+                        DoZoom();
+                    }
+                }
+                break;
+
+            case SystemDecorationsBorderOnly:
+                [Window setHasShadow:YES];
+                [Window setTitleVisibility:NSWindowTitleHidden];
+                [Window setTitlebarAppearsTransparent:YES];
+
+                if (currentWindowState == Maximized) {
+                    if (!UndecoratedIsMaximized()) {
+                        DoZoom();
+                    }
+                }
+                break;
+
+            case SystemDecorationsFull:
+                [Window setHasShadow:YES];
+                [Window setTitleVisibility:NSWindowTitleVisible];
+                [Window setTitlebarAppearsTransparent:NO];
+                [Window setTitle:_lastTitle];
+
+                if (currentWindowState == Maximized) {
+                    auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
+
+                    [View setFrameSize:newFrame];
+                }
+                break;
+        }
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::SetTitle(char *utf8title) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        _lastTitle = [NSString stringWithUTF8String:(const char *) utf8title];
+        [Window setTitle:_lastTitle];
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::SetTitleBarColor(AvnColor color) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        float a = (float) color.Alpha / 255.0f;
+        float r = (float) color.Red / 255.0f;
+        float g = (float) color.Green / 255.0f;
+        float b = (float) color.Blue / 255.0f;
+
+        auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a];
+
+        // Based on the titlebar color we have to choose either light or dark
+        // OSX doesnt let you set a foreground color for titlebar.
+        if ((r * 0.299 + g * 0.587 + b * 0.114) > 186.0f / 255.0f) {
+            [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]];
+        } else {
+            [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
+        }
+
+        [Window setTitlebarAppearsTransparent:true];
+        [Window setBackgroundColor:nscolor];
+    }
+
+    return S_OK;
+}
+
+HRESULT WindowImpl::GetWindowState(AvnWindowState *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr) {
+            return E_POINTER;
+        }
+
+        if (([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) {
+            *ret = FullScreen;
+            return S_OK;
+        }
+
+        if ([Window isMiniaturized]) {
+            *ret = Minimized;
+            return S_OK;
+        }
+
+        if (IsZoomed()) {
+            *ret = Maximized;
+            return S_OK;
+        }
+
+        *ret = Normal;
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::TakeFocusFromChildren() {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (Window == nil)
+            return S_OK;
+        if ([Window isKeyWindow])
+            [Window makeFirstResponder:View];
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::SetExtendClientArea(bool enable) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        _isClientAreaExtended = enable;
+
+        if(Window != nullptr) {
+            if (enable) {
+                Window.titleVisibility = NSWindowTitleHidden;
+
+                [Window setTitlebarAppearsTransparent:true];
+
+                auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
+
+                if (wantsTitleBar) {
+                    [StandardContainer ShowTitleBar:true];
+                } else {
+                    [StandardContainer ShowTitleBar:false];
+                }
+
+                if (_extendClientHints & AvnOSXThickTitleBar) {
+                    Window.toolbar = [NSToolbar new];
+                    Window.toolbar.showsBaselineSeparator = false;
+                } else {
+                    Window.toolbar = nullptr;
+                }
+            } else {
+                Window.titleVisibility = NSWindowTitleVisible;
+                Window.toolbar = nullptr;
+                [Window setTitlebarAppearsTransparent:false];
+                View.layer.zPosition = 0;
+            }
+
+            [GetWindowProtocol() setIsExtended:enable];
+
+            HideOrShowTrafficLights();
+
+            UpdateStyle();
+        }
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::SetExtendClientAreaHints(AvnExtendClientAreaChromeHints hints) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        _extendClientHints = hints;
+
+        SetExtendClientArea(_isClientAreaExtended);
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr) {
+            return E_POINTER;
+        }
+
+        *ret = [GetWindowProtocol() getExtendedTitleBarHeight];
+
+        return S_OK;
+    }
+}
+
+HRESULT WindowImpl::SetExtendTitleBarHeight(double value) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        [StandardContainer SetTitleBarHeightHint:value];
+        return S_OK;
+    }
+}
+
+void WindowImpl::EnterFullScreenMode() {
+    _fullScreenActive = true;
+
+    [Window setTitle:_lastTitle];
+    [Window toggleFullScreen:nullptr];
+}
+
+void WindowImpl::ExitFullScreenMode() {
+    [Window toggleFullScreen:nullptr];
+
+    _fullScreenActive = false;
+
+    SetDecorations(_decorations);
+}
+
+HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (Window == nullptr) {
+            return S_OK;
+        }
+
+        if (_actualWindowState == state) {
+            return S_OK;
+        }
+
+        _inSetWindowState = true;
+
+        auto currentState = _actualWindowState;
+        _lastWindowState = state;
+
+        if (currentState == Normal) {
+            _preZoomSize = [Window frame];
+        }
+
+        if (_shown) {
+            switch (state) {
+                case Maximized:
+                    if (currentState == FullScreen) {
+                        ExitFullScreenMode();
+                    }
+
+                    lastPositionSet.X = 0;
+                    lastPositionSet.Y = 0;
+
+                    if ([Window isMiniaturized]) {
+                        [Window deminiaturize:Window];
+                    }
+
+                    if (!IsZoomed()) {
+                        DoZoom();
+                    }
+                    break;
+
+                case Minimized:
+                    if (currentState == FullScreen) {
+                        ExitFullScreenMode();
+                    } else {
+                        [Window miniaturize:Window];
+                    }
+                    break;
+
+                case FullScreen:
+                    if ([Window isMiniaturized]) {
+                        [Window deminiaturize:Window];
+                    }
+
+                    EnterFullScreenMode();
+                    break;
+
+                case Normal:
+                    if ([Window isMiniaturized]) {
+                        [Window deminiaturize:Window];
+                    }
+
+                    if (currentState == FullScreen) {
+                        ExitFullScreenMode();
+                    }
+
+                    if (IsZoomed()) {
+                        if (_decorations == SystemDecorationsFull) {
+                            DoZoom();
+                        } else {
+                            [Window setFrame:_preZoomSize display:true];
+                            auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
+
+                            [View setFrameSize:newFrame];
+                        }
+
+                    }
+                    break;
+            }
+
+            _actualWindowState = _lastWindowState;
+            WindowEvents->WindowStateChanged(_actualWindowState);
+        }
+
+
+        _inSetWindowState = false;
+
+        return S_OK;
+    }
+}
+
+bool WindowImpl::IsDialog() {
+    return _isDialog;
+}
+
+NSWindowStyleMask WindowImpl::GetStyle() {
+    unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless;
+
+    switch (_decorations) {
+        case SystemDecorationsNone:
+            s = s | NSWindowStyleMaskFullSizeContentView;
+            break;
+
+        case SystemDecorationsBorderOnly:
+            s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView;
+            break;
+
+        case SystemDecorationsFull:
+            s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless;
+
+            if (_canResize) {
+                s = s | NSWindowStyleMaskResizable;
+            }
+            break;
+    }
+
+    if ([Window parentWindow] == nullptr) {
+        s |= NSWindowStyleMaskMiniaturizable;
+    }
+
+    if (_isClientAreaExtended) {
+        s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground;
+    }
+    return s;
+}

+ 17 - 0
native/Avalonia.Native/src/OSX/WindowInterfaces.h

@@ -0,0 +1,17 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <AppKit/AppKit.h>
+#include "WindowProtocol.h"
+#include "WindowBaseImpl.h"
+
+@interface AvnWindow : NSWindow <AvnWindowProtocol, NSWindowDelegate>
+-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+@end
+
+@interface AvnPanel : NSPanel <AvnWindowProtocol, NSWindowDelegate>
+-(AvnPanel* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+@end

+ 25 - 0
native/Avalonia.Native/src/OSX/WindowProtocol.h

@@ -0,0 +1,25 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#pragma once
+
+#import <AppKit/AppKit.h>
+
+@class AvnMenu;
+
+@protocol AvnWindowProtocol
+-(void) pollModalSession: (NSModalSession _Nonnull) session;
+-(void) restoreParentWindow;
+-(bool) shouldTryToHandleEvents;
+-(void) setEnabled: (bool) enable;
+-(void) showAppMenuOnly;
+-(void) showWindowMenuWithAppMenu;
+-(void) applyMenu:(AvnMenu* _Nullable)menu;
+
+-(double) getExtendedTitleBarHeight;
+-(void) setIsExtended:(bool)value;
+-(void) disconnectParent;
+-(bool) isDialog;
+@end

+ 0 - 12
native/Avalonia.Native/src/OSX/app.mm

@@ -82,18 +82,6 @@ ComPtr<IAvnApplicationEvents> _events;
         _isHandlingSendEvent = oldHandling;
     }
 }
-
-// This is needed for certain embedded controls
-- (BOOL) isHandlingSendEvent
-{
-    return _isHandlingSendEvent;
-}
-
-- (void)setHandlingSendEvent:(BOOL)handlingSendEvent
-{
-    _isHandlingSendEvent = handlingSendEvent;
-}
-
 @end
 
 extern void InitializeAvnApp(IAvnApplicationEvents* events)

+ 2 - 2
native/Avalonia.Native/src/OSX/automation.h

@@ -1,6 +1,6 @@
-#import <Cocoa/Cocoa.h>
-#include "window.h"
+#pragma once
 
+#import <Cocoa/Cocoa.h>
 NS_ASSUME_NONNULL_BEGIN
 
 class IAvnAutomationPeer;

+ 2 - 1
native/Avalonia.Native/src/OSX/automation.mm

@@ -1,7 +1,8 @@
 #include "common.h"
 #include "automation.h"
 #include "AvnString.h"
-#include "window.h"
+#include "INSWindowHolder.h"
+#include "AvnView.h"
 
 @interface AvnAccessibilityElement (Events)
 - (void) raiseChildrenChanged;

+ 1 - 2
native/Avalonia.Native/src/OSX/common.h

@@ -27,7 +27,7 @@ extern IAvnMenuItem* CreateAppMenuItem();
 extern IAvnMenuItem* CreateAppMenuItemSeparator();
 extern IAvnApplicationCommands* CreateApplicationCommands();
 extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
-extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu);
+extern void SetAppMenu(IAvnMenu *menu);
 extern void SetServicesMenu (IAvnMenu* menu);
 extern IAvnMenu* GetAppMenu ();
 extern NSMenuItem* GetAppMenuItem ();
@@ -38,7 +38,6 @@ extern NSPoint ToNSPoint (AvnPoint p);
 extern NSRect ToNSRect (AvnRect r);
 extern AvnPoint ToAvnPoint (NSPoint p);
 extern AvnPoint ConvertPointY (AvnPoint p);
-extern CGFloat PrimaryDisplayHeight();
 extern NSSize ToNSSize (AvnSize s);
 #ifdef DEBUG
 #define NSDebugLog(...) NSLog(__VA_ARGS__)

+ 0 - 1
native/Avalonia.Native/src/OSX/cursor.mm

@@ -1,6 +1,5 @@
 #include "common.h"
 #include "cursor.h"
-#include <map>
 
 class CursorFactory : public ComSingleObject<IAvnCursorFactory, &IID_IAvnCursorFactory>
 {

+ 1 - 6
native/Avalonia.Native/src/OSX/main.mm

@@ -1,7 +1,6 @@
 //This file will contain actual IID structures
 #define COM_GUIDS_MATERIALIZE
 #include "common.h"
-#include "window.h"
 
 static NSString* s_appTitle = @"Avalonia";
 
@@ -343,7 +342,7 @@ public:
         
         @autoreleasepool
         {
-            ::SetAppMenu(s_appTitle, appMenu);
+            ::SetAppMenu(appMenu);
             return S_OK;
         }
     }
@@ -428,7 +427,3 @@ AvnPoint ConvertPointY (AvnPoint p)
     return p;
 }
 
-CGFloat PrimaryDisplayHeight()
-{
-  return NSMaxY([[[NSScreen screens] firstObject] frame]);
-}

+ 0 - 1
native/Avalonia.Native/src/OSX/menu.h

@@ -31,7 +31,6 @@ private:
     NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem
     IAvnActionCallback* _callback;
     IAvnPredicateCallback* _predicate;
-    bool _isSeparator;
     bool _isCheckable;
     
 public:

+ 2 - 4
native/Avalonia.Native/src/OSX/menu.mm

@@ -1,7 +1,6 @@
 
 #include "common.h"
 #include "menu.h"
-#include "window.h"
 #include "KeyTransform.h"
 #include <CoreFoundation/CoreFoundation.h>
 #include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */
@@ -74,8 +73,7 @@
 AvnAppMenuItem::AvnAppMenuItem(bool isSeparator)
 {
     _isCheckable = false;
-    _isSeparator = isSeparator;
-    
+
     if(isSeparator)
     {
         _native = [NSMenuItem separatorItem];
@@ -460,7 +458,7 @@ extern IAvnMenuItem* CreateAppMenuItemSeparator()
 static IAvnMenu* s_appMenu = nullptr;
 static NSMenuItem* s_appMenuItem = nullptr;
 
-extern void SetAppMenu (NSString* appName, IAvnMenu* menu)
+extern void SetAppMenu(IAvnMenu *menu)
 {
     s_appMenu = menu;
     

+ 0 - 4
native/Avalonia.Native/src/OSX/rendertarget.mm

@@ -1,14 +1,10 @@
 #include "common.h"
 #include "rendertarget.h"
-#import <IOSurface/IOSurface.h>
 #import <IOSurface/IOSurfaceObjC.h>
 #import <QuartzCore/QuartzCore.h>
 
-#include <OpenGL/CGLIOSurface.h>
-#include <OpenGL/OpenGL.h>
 #include <OpenGL/glext.h>
 #include <OpenGL/gl3.h>
-#include <OpenGL/gl3ext.h>
 
 @interface IOSurfaceHolder : NSObject
 @end

+ 0 - 77
native/Avalonia.Native/src/OSX/window.h

@@ -1,77 +0,0 @@
-#ifndef window_h
-#define window_h
-
-class WindowBaseImpl;
-
-@interface AvnView : NSView<NSTextInputClient, NSDraggingDestination>
--(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
--(NSEvent* _Nonnull) lastMouseDownEvent;
--(AvnPoint) translateLocalPoint:(AvnPoint)pt;
--(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose;
--(void) onClosed;
--(AvnPixelSize) getPixelSize;
--(AvnPlatformResizeReason) getResizeReason;
--(void) setResizeReason:(AvnPlatformResizeReason)reason;
-+ (AvnPoint)toAvnPoint:(CGPoint)p;
-@end
-
-@interface AutoFitContentView : NSView
--(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content;
--(void) ShowTitleBar: (bool) show;
--(void) SetTitleBarHeightHint: (double) height;
--(void) SetContent: (NSView* _Nonnull) content;
--(void) ShowBlur: (bool) show;
-@end
-
-@interface AvnWindow : NSWindow <NSWindowDelegate>
-+(void) closeAll;
--(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
--(void) setCanBecomeKeyAndMain;
--(void) pollModalSession: (NSModalSession _Nonnull) session;
--(void) restoreParentWindow;
--(bool) shouldTryToHandleEvents;
--(void) setEnabled: (bool) enable;
--(void) showAppMenuOnly;
--(void) showWindowMenuWithAppMenu;
--(void) applyMenu:(NSMenu* _Nullable)menu;
--(double) getScaling;
--(double) getExtendedTitleBarHeight;
--(void) setIsExtended:(bool)value;
--(bool) isDialog;
-@end
-
-struct INSWindowHolder
-{
-    virtual AvnWindow* _Nonnull GetNSWindow () = 0;
-    virtual AvnView* _Nonnull GetNSView () = 0;
-};
-
-struct IWindowStateChanged
-{
-    virtual void WindowStateChanged () = 0;
-    virtual void StartStateTransition () = 0;
-    virtual void EndStateTransition () = 0;
-    virtual SystemDecorations Decorations () = 0;
-    virtual AvnWindowState WindowState () = 0;
-};
-
-class ResizeScope
-{
-public:
-    ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason)
-    {
-        _view = view;
-        _restore = [view getResizeReason];
-        [view setResizeReason:reason];
-    }
-    
-    ~ResizeScope()
-    {
-        [_view setResizeReason:_restore];
-    }
-private:
-    AvnView* _Nonnull _view;
-    AvnPlatformResizeReason _restore;
-};
-
-#endif /* window_h */

+ 0 - 2590
native/Avalonia.Native/src/OSX/window.mm

@@ -1,2590 +0,0 @@
-#include "common.h"
-#include "window.h"
-#include "KeyTransform.h"
-#include "cursor.h"
-#include "menu.h"
-#include <OpenGL/gl.h>
-#include "rendertarget.h"
-#include "AvnString.h"
-#include "automation.h"
-
-class WindowBaseImpl : public virtual ComObject,
-    public virtual IAvnWindowBase,
-    public INSWindowHolder
-{
-private:
-    NSCursor* cursor;
-
-public:
-    FORWARD_IUNKNOWN()
-    BEGIN_INTERFACE_MAP()
-    INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase)
-    END_INTERFACE_MAP()
-
-    virtual ~WindowBaseImpl()
-    {
-        View = NULL;
-        Window = NULL;
-    }
-    AutoFitContentView* StandardContainer;
-    AvnView* View;
-    AvnWindow* Window;
-    ComPtr<IAvnWindowBaseEvents> BaseEvents;
-    ComPtr<IAvnGlContext> _glContext;
-    NSObject<IRenderTarget>* renderTarget;
-    AvnPoint lastPositionSet;
-    NSString* _lastTitle;
-    IAvnMenu* _mainMenu;
-    
-    bool _shown;
-    bool _inResize;
-    
-    WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl)
-    {
-        _shown = false;
-        _inResize = false;
-        _mainMenu = nullptr;
-        BaseEvents = events;
-        _glContext = gl;
-        renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl];
-        View = [[AvnView alloc] initWithParent:this];
-        StandardContainer = [[AutoFitContentView new] initWithContent:View];
-
-        Window = [[AvnWindow alloc] initWithParent:this];
-        [Window setContentView: StandardContainer];
-        
-        lastPositionSet.X = 100;
-        lastPositionSet.Y = 100;
-        _lastTitle = @"";
-        
-        [Window setStyleMask:NSWindowStyleMaskBorderless];
-        [Window setBackingType:NSBackingStoreBuffered];
-        
-        [Window setOpaque:false];
-    }
-    
-    virtual HRESULT ObtainNSWindowHandle(void** ret) override
-    {
-        START_COM_CALL;
-        
-        if (ret == nullptr)
-        {
-            return E_POINTER;
-        }
-        
-        *ret =  (__bridge void*)Window;
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT ObtainNSWindowHandleRetained(void** ret) override
-    {
-        START_COM_CALL;
-        
-        if (ret == nullptr)
-        {
-            return E_POINTER;
-        }
-        
-        *ret =  (__bridge_retained void*)Window;
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT ObtainNSViewHandle(void** ret) override
-    {
-        START_COM_CALL;
-        
-        if (ret == nullptr)
-        {
-            return E_POINTER;
-        }
-        
-        *ret =  (__bridge void*)View;
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT ObtainNSViewHandleRetained(void** ret) override
-    {
-        START_COM_CALL;
-        
-        if (ret == nullptr)
-        {
-            return E_POINTER;
-        }
-        
-        *ret =  (__bridge_retained void*)View;
-        
-        return S_OK;
-    }
-    
-    virtual AvnWindow* GetNSWindow() override
-    {
-        return Window;
-    }
-
-    virtual AvnView* GetNSView() override
-    {
-        return View;
-    }
-
-    virtual HRESULT Show(bool activate, bool isDialog) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            SetPosition(lastPositionSet);
-            UpdateStyle();
-            
-            [Window setTitle:_lastTitle];
-            
-            if(ShouldTakeFocusOnShow() && activate)
-            {
-                [Window orderFront: Window];
-                [Window makeKeyAndOrderFront:Window];
-                [Window makeFirstResponder:View];
-                [NSApp activateIgnoringOtherApps:YES];
-            }
-            else
-            {
-                [Window orderFront: Window];
-            }
-            
-            _shown = true;
-            
-            return S_OK;
-        }
-    }
-    
-    virtual bool ShouldTakeFocusOnShow()
-    {
-        return true;
-    }
-    
-    virtual HRESULT Hide () override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(Window != nullptr)
-            {
-                [Window orderOut:Window];
-                [Window restoreParentWindow];
-            }
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT Activate () override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(Window != nullptr)
-            {
-                [Window makeKeyAndOrderFront:nil];
-                [NSApp activateIgnoringOtherApps:YES];
-            }
-        }
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT SetTopMost (bool value) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel];
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT Close() override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if (Window != nullptr)
-            {
-                auto window = Window;
-                Window = nullptr;
-                
-                try{
-                    // Seems to throw sometimes on application exit.
-                    [window close];
-                }
-                catch(NSException*){}
-            }
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT GetClientSize(AvnSize* ret) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(ret == nullptr)
-                return E_POINTER;
-            
-            auto frame = [View frame];
-            ret->Width = frame.size.width;
-            ret->Height = frame.size.height;
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT GetFrameSize(AvnSize* ret) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(ret == nullptr)
-                return E_POINTER;
-            
-            auto frame = [Window frame];
-            ret->Width = frame.size.width;
-            ret->Height = frame.size.height;
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT GetScaling (double* ret) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(ret == nullptr)
-                return E_POINTER;
-            
-            if(Window == nullptr)
-            {
-                *ret = 1;
-                return S_OK;
-            }
-            
-            *ret = [Window backingScaleFactor];
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            [Window setMinSize: ToNSSize(minSize)];
-            [Window setMaxSize: ToNSSize(maxSize)];
-        
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
-    {
-        if(_inResize)
-        {
-            return S_OK;
-        }
-        
-        _inResize = true;
-        
-        START_COM_CALL;
-        auto resizeBlock = ResizeScope(View, reason);
-        
-        @autoreleasepool
-        {
-            auto maxSize = [Window maxSize];
-            auto minSize = [Window minSize];
-            
-            if (x < minSize.width)
-            {
-                x = minSize.width;
-            }
-            
-            if (y < minSize.height)
-            {
-                y = minSize.height;
-            }
-            
-            if (x > maxSize.width)
-            {
-                x = maxSize.width;
-            }
-            
-            if (y > maxSize.height)
-            {
-                y = maxSize.height;
-            }
-            
-            @try
-            {
-                if(!_shown)
-                {
-                    BaseEvents->Resized(AvnSize{x,y}, reason);
-                }
-                
-                [Window setContentSize:NSSize{x,y}];
-                [Window invalidateShadow];
-            }
-            @finally
-            {
-                _inResize = false;
-            }
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT Invalidate (AvnRect rect) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            [View setNeedsDisplayInRect:[View frame]];
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetMainMenu(IAvnMenu* menu) override
-    {
-        START_COM_CALL;
-        
-        _mainMenu = menu;
-        
-        auto nativeMenu = dynamic_cast<AvnAppMenu*>(menu);
-        
-        auto nsmenu = nativeMenu->GetNative();
-        
-        [Window applyMenu:nsmenu];
-        
-        if ([Window isKeyWindow])
-        {
-            [Window showWindowMenuWithAppMenu];
-        }
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT BeginMoveDrag () override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            auto lastEvent = [View lastMouseDownEvent];
-            
-            if(lastEvent == nullptr)
-            {
-                return S_OK;
-            }
-            
-            [Window performWindowDragWithEvent:lastEvent];
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) override
-    {
-        START_COM_CALL;
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT GetPosition (AvnPoint* ret) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(ret == nullptr)
-            {
-                return E_POINTER;
-            }
-            
-            auto frame = [Window frame];
-            
-            ret->X = frame.origin.x;
-            ret->Y = frame.origin.y + frame.size.height;
-            
-            *ret = ConvertPointY(*ret);
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetPosition (AvnPoint point) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            lastPositionSet = point;
-            [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(ret == nullptr)
-            {
-                return E_POINTER;
-            }
-            
-            point = ConvertPointY(point);
-            NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
-            auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
-            
-            *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(ret == nullptr)
-            {
-                return E_POINTER;
-            }
-            
-            auto cocoaViewPoint =  ToNSPoint([View translateLocalPoint:point]);
-            NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)];
-            auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y));
-            *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) override
-    {
-        START_COM_CALL;
-        
-        [View setSwRenderedFrame: fb dispose: dispose];
-        return S_OK;
-    }
-    
-    virtual HRESULT SetCursor(IAvnCursor* cursor) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            Cursor* avnCursor = dynamic_cast<Cursor*>(cursor);
-            this->cursor = avnCursor->GetNative();
-            UpdateCursor();
-            
-            if(avnCursor->IsHidden())
-            {
-                [NSCursor hide];
-            }
-            else
-            {
-                [NSCursor unhide];
-            }
-            
-            return S_OK;
-        }
-    }
-
-    virtual void UpdateCursor()
-    {
-        if (cursor != nil)
-        {
-            [cursor set];
-        }
-    }
-    
-    virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) override
-    {
-        START_COM_CALL;
-        
-        if(View == NULL)
-            return E_FAIL;
-        *ppv = [renderTarget createSurfaceRenderTarget];
-        return *ppv == nil ? E_FAIL : S_OK;
-    }
-    
-    virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) override
-    {
-        START_COM_CALL;
-        
-        if(View == NULL)
-            return E_FAIL;
-        *retOut = ::CreateNativeControlHost(View);
-        return S_OK;
-    }
-    
-    virtual HRESULT SetBlurEnabled (bool enable) override
-    {
-        START_COM_CALL;
-        
-        [StandardContainer ShowBlur:enable];
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
-                                              IAvnClipboard* clipboard, IAvnDndResultCallback* cb,
-                                              void* sourceHandle) override
-    {
-        START_COM_CALL;
-        
-        auto item = TryGetPasteboardItem(clipboard);
-        [item setString:@"" forType:GetAvnCustomDataType()];
-        if(item == nil)
-            return E_INVALIDARG;
-        if(View == NULL)
-            return E_FAIL;
-        
-        auto nsevent = [NSApp currentEvent];
-        auto nseventType = [nsevent type];
-        
-        // If current event isn't a mouse one (probably due to malfunctioning user app)
-        // attempt to forge a new one
-        if(!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited)
-           || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged)))
-        {
-            NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
-            auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);            
-            CGPoint cgpoint = NSPointToCGPoint(nspoint);
-            auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft);
-            nsevent = [NSEvent eventWithCGEvent: cgevent];
-            CFRelease(cgevent);
-        }
-        
-        auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: item];
-        
-        auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments];
-        NSRect dragItemRect = {(float)point.X, (float)point.Y, [dragItemImage size].width, [dragItemImage size].height};
-        [dragItem setDraggingFrame: dragItemRect contents: dragItemImage];
-        
-        int op = 0; int ieffects = (int)effects;
-        if((ieffects & (int)AvnDragDropEffects::Copy) != 0)
-            op |= NSDragOperationCopy;
-        if((ieffects & (int)AvnDragDropEffects::Link) != 0)
-            op |= NSDragOperationLink;
-        if((ieffects & (int)AvnDragDropEffects::Move) != 0)
-            op |= NSDragOperationMove;
-        [View beginDraggingSessionWithItems: @[dragItem] event: nsevent
-                                     source: CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)];
-        return S_OK;
-    }
-
-    virtual bool IsDialog()
-    {
-        return false;
-    }
-    
-protected:
-    virtual NSWindowStyleMask GetStyle()
-    {
-        return NSWindowStyleMaskBorderless;
-    }
-    
-    void UpdateStyle()
-    {
-        [Window setStyleMask: GetStyle()];
-    }
-    
-public:
-    virtual void OnResized ()
-    {
-        
-    }
-};
-
-class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
-{
-private:
-    bool _canResize;
-    bool _fullScreenActive;
-    SystemDecorations _decorations;
-    AvnWindowState _lastWindowState;
-    AvnWindowState _actualWindowState;
-    bool _inSetWindowState;
-    NSRect _preZoomSize;
-    bool _transitioningWindowState;
-    bool _isClientAreaExtended;
-    bool _isDialog;
-    AvnExtendClientAreaChromeHints _extendClientHints;
-    
-    FORWARD_IUNKNOWN()
-    BEGIN_INTERFACE_MAP()
-    INHERIT_INTERFACE_MAP(WindowBaseImpl)
-    INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow)
-    END_INTERFACE_MAP()
-    virtual ~WindowImpl()
-    {
-    }
-    
-    ComPtr<IAvnWindowEvents> WindowEvents;
-    WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
-    {
-        _isClientAreaExtended = false;
-        _extendClientHints = AvnDefaultChrome;
-        _fullScreenActive = false;
-        _canResize = true;
-        _decorations = SystemDecorationsFull;
-        _transitioningWindowState = false;
-        _inSetWindowState = false;
-        _lastWindowState = Normal;
-        _actualWindowState = Normal;
-        WindowEvents = events;
-        [Window setCanBecomeKeyAndMain];
-        [Window disableCursorRects];
-        [Window setTabbingMode:NSWindowTabbingModeDisallowed];
-        [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
-    }
-    
-    void HideOrShowTrafficLights ()
-    {
-        if (Window == nil)
-        {
-            return;
-        }
-        
-        for (id subview in Window.contentView.superview.subviews) {
-            if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
-                NSView *titlebarView = [subview subviews][0];
-                for (id button in titlebarView.subviews) {
-                    if ([button isKindOfClass:[NSButton class]])
-                    {
-                        if(_isClientAreaExtended)
-                        {
-                            auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
-                            
-                            [button setHidden: !wantsChrome];
-                        }
-                        else
-                        {
-                            [button setHidden: (_decorations != SystemDecorationsFull)];
-                        }
-                        
-                        [button setWantsLayer:true];
-                    }
-                }
-            }
-        }
-    }
-    
-    virtual HRESULT Show (bool activate, bool isDialog) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            _isDialog = isDialog;
-            WindowBaseImpl::Show(activate, isDialog);
-            
-            HideOrShowTrafficLights();
-            
-            return SetWindowState(_lastWindowState);
-        }
-    }
-    
-    virtual HRESULT SetEnabled (bool enable) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            [Window setEnabled:enable];
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetParent (IAvnWindow* parent) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(parent == nullptr)
-                return E_POINTER;
-
-            auto cparent = dynamic_cast<WindowImpl*>(parent);
-            if(cparent == nullptr)
-                return E_INVALIDARG;
-            
-            // If one tries to show a child window with a minimized parent window, then the parent window will be
-            // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
-            // state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
-            if (cparent->WindowState() == Minimized)
-                cparent->SetWindowState(Normal);
-            
-            [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
-            [cparent->Window addChildWindow:Window ordered:NSWindowAbove];
-            
-            UpdateStyle();
-            
-            return S_OK;
-        }
-    }
-    
-    void StartStateTransition () override
-    {
-        _transitioningWindowState = true;
-    }
-    
-    void EndStateTransition () override
-    {
-        _transitioningWindowState = false;
-    }
-    
-    SystemDecorations Decorations () override
-    {
-        return _decorations;
-    }
-    
-    AvnWindowState WindowState () override
-    {
-        return _lastWindowState;
-    }
-    
-    void WindowStateChanged () override
-    {
-        if(_shown && !_inSetWindowState && !_transitioningWindowState)
-        {
-            AvnWindowState state;
-            GetWindowState(&state);
-            
-            if(_lastWindowState != state)
-            {
-                if(_isClientAreaExtended)
-                {
-                    if(_lastWindowState == FullScreen)
-                    {
-                        // we exited fs.
-                       if(_extendClientHints & AvnOSXThickTitleBar)
-                       {
-                          Window.toolbar = [NSToolbar new];
-                          Window.toolbar.showsBaselineSeparator = false;
-                       }
-
-                       [Window setTitlebarAppearsTransparent:true];
-
-                       [StandardContainer setFrameSize: StandardContainer.frame.size];
-                    }
-                    else if(state == FullScreen)
-                    {
-                        // we entered fs.
-                        if(_extendClientHints & AvnOSXThickTitleBar)
-                        {
-                            Window.toolbar = nullptr;
-                        }
-                       
-                        [Window setTitlebarAppearsTransparent:false];
-                        
-                        [StandardContainer setFrameSize: StandardContainer.frame.size];
-                    }
-                }
-                
-                _lastWindowState = state;
-                _actualWindowState = state;
-                WindowEvents->WindowStateChanged(state);
-            }
-        }
-    }
-    
-    bool UndecoratedIsMaximized ()
-    {
-        auto windowSize = [Window frame];
-        auto available = [Window screen].visibleFrame;
-        return CGRectEqualToRect(windowSize, available);
-    }
-    
-    bool IsZoomed ()
-    {
-        return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized();
-    }
-    
-    void DoZoom()
-    {
-        switch (_decorations)
-        {
-            case SystemDecorationsNone:
-            case SystemDecorationsBorderOnly:
-                [Window setFrame:[Window screen].visibleFrame display:true];
-                break;
-
-            
-            case SystemDecorationsFull:
-                [Window performZoom:Window];
-                break;
-        }
-    }
-    
-    virtual HRESULT SetCanResize(bool value) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            _canResize = value;
-            UpdateStyle();
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetDecorations(SystemDecorations value) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            auto currentWindowState = _lastWindowState;
-            _decorations = value;
-            
-            if(_fullScreenActive)
-            {
-                return S_OK;
-            }
-            
-            UpdateStyle();
-            
-            HideOrShowTrafficLights();
-
-            switch (_decorations)
-            {
-                case SystemDecorationsNone:
-                    [Window setHasShadow:NO];
-                    [Window setTitleVisibility:NSWindowTitleHidden];
-                    [Window setTitlebarAppearsTransparent:YES];
-                    
-                    if(currentWindowState == Maximized)
-                    {
-                        if(!UndecoratedIsMaximized())
-                        {
-                            DoZoom();
-                        }
-                    }
-                    break;
-
-                case SystemDecorationsBorderOnly:
-                    [Window setHasShadow:YES];
-                    [Window setTitleVisibility:NSWindowTitleHidden];
-                    [Window setTitlebarAppearsTransparent:YES];
-                    
-                    if(currentWindowState == Maximized)
-                    {
-                        if(!UndecoratedIsMaximized())
-                        {
-                            DoZoom();
-                        }
-                    }
-                    break;
-
-                case SystemDecorationsFull:
-                    [Window setHasShadow:YES];
-                    [Window setTitleVisibility:NSWindowTitleVisible];
-                    [Window setTitlebarAppearsTransparent:NO];
-                    [Window setTitle:_lastTitle];
-                    
-                    if(currentWindowState == Maximized)
-                    {
-                        auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
-                        
-                        [View setFrameSize:newFrame];
-                    }
-                    break;
-            }
-
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetTitle (char* utf8title) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            _lastTitle = [NSString stringWithUTF8String:(const char*)utf8title];
-            [Window setTitle:_lastTitle];
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetTitleBarColor(AvnColor color) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            float a = (float)color.Alpha / 255.0f;
-            float r = (float)color.Red / 255.0f;
-            float g = (float)color.Green / 255.0f;
-            float b = (float)color.Blue / 255.0f;
-            
-            auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a];
-            
-            // Based on the titlebar color we have to choose either light or dark
-            // OSX doesnt let you set a foreground color for titlebar.
-            if ((r*0.299 + g*0.587 + b*0.114) > 186.0f / 255.0f)
-            {
-                [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]];
-            }
-            else
-            {
-                [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
-            }
-            
-            [Window setTitlebarAppearsTransparent:true];
-            [Window setBackgroundColor:nscolor];
-        }
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT GetWindowState (AvnWindowState*ret) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(ret == nullptr)
-            {
-                return E_POINTER;
-            }
-            
-            if(([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen)
-            {
-                *ret = FullScreen;
-                return S_OK;
-            }
-            
-            if([Window isMiniaturized])
-            {
-                *ret = Minimized;
-                return S_OK;
-            }
-            
-            if(IsZoomed())
-            {
-                *ret = Maximized;
-                return S_OK;
-            }
-            
-            *ret = Normal;
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT TakeFocusFromChildren () override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(Window == nil)
-                return S_OK;
-            if([Window isKeyWindow])
-                [Window makeFirstResponder: View];
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetExtendClientArea (bool enable) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            _isClientAreaExtended = enable;
-            
-            if(enable)
-            {
-                Window.titleVisibility = NSWindowTitleHidden;
-                
-                [Window setTitlebarAppearsTransparent:true];
-                
-                auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
-                
-                if (wantsTitleBar)
-                {
-                    [StandardContainer ShowTitleBar:true];
-                }
-                else
-                {
-                    [StandardContainer ShowTitleBar:false];
-                }
-                
-                if(_extendClientHints & AvnOSXThickTitleBar)
-                {
-                    Window.toolbar = [NSToolbar new];
-                    Window.toolbar.showsBaselineSeparator = false;
-                }
-                else
-                {
-                    Window.toolbar = nullptr;
-                }
-            }
-            else
-            {
-                Window.titleVisibility = NSWindowTitleVisible;
-                Window.toolbar = nullptr;
-                [Window setTitlebarAppearsTransparent:false];
-                View.layer.zPosition = 0;
-            }
-            
-            [Window setIsExtended:enable];
-            
-            HideOrShowTrafficLights();
-            
-            UpdateStyle();
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            _extendClientHints = hints;
-            
-            SetExtendClientArea(_isClientAreaExtended);
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT GetExtendTitleBarHeight (double*ret) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(ret == nullptr)
-            {
-                return E_POINTER;
-            }
-            
-            *ret = [Window getExtendedTitleBarHeight];
-            
-            return S_OK;
-        }
-    }
-    
-    virtual HRESULT SetExtendTitleBarHeight (double value) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            [StandardContainer SetTitleBarHeightHint:value];
-            return S_OK;
-        }
-    }
-    
-    void EnterFullScreenMode ()
-    {
-        _fullScreenActive = true;
-        
-        [Window setTitle:_lastTitle];
-        [Window toggleFullScreen:nullptr];
-    }
-    
-    void ExitFullScreenMode ()
-    {
-        [Window toggleFullScreen:nullptr];
-        
-        _fullScreenActive = false;
-        
-        SetDecorations(_decorations);
-    }
-    
-    virtual HRESULT SetWindowState (AvnWindowState state) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if(Window == nullptr)
-            {
-                return  S_OK;
-            }
-            
-            if(_actualWindowState == state)
-            {
-                return S_OK;
-            }
-            
-            _inSetWindowState = true;
-            
-            auto currentState = _actualWindowState;
-            _lastWindowState = state;
-            
-            if(currentState == Normal)
-            {
-                _preZoomSize = [Window frame];
-            }
-            
-            if(_shown)
-            {
-                switch (state) {
-                    case Maximized:
-                        if(currentState == FullScreen)
-                        {
-                            ExitFullScreenMode();
-                        }
-                        
-                        lastPositionSet.X = 0;
-                        lastPositionSet.Y = 0;
-                        
-                        if([Window isMiniaturized])
-                        {
-                            [Window deminiaturize:Window];
-                        }
-                        
-                        if(!IsZoomed())
-                        {
-                            DoZoom();
-                        }
-                        break;
-                        
-                    case Minimized:
-                        if(currentState == FullScreen)
-                        {
-                            ExitFullScreenMode();
-                        }
-                        else
-                        {
-                            [Window miniaturize:Window];
-                        }
-                        break;
-                        
-                    case FullScreen:
-                        if([Window isMiniaturized])
-                        {
-                            [Window deminiaturize:Window];
-                        }
-                        
-                        EnterFullScreenMode();
-                        break;
-                        
-                    case Normal:
-                        if([Window isMiniaturized])
-                        {
-                            [Window deminiaturize:Window];
-                        }
-                        
-                        if(currentState == FullScreen)
-                        {
-                            ExitFullScreenMode();
-                        }
-                        
-                        if(IsZoomed())
-                        {
-                            if(_decorations == SystemDecorationsFull)
-                            {
-                                DoZoom();
-                            }
-                            else
-                            {
-                                [Window setFrame:_preZoomSize display:true];
-                                auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
-                                
-                                [View setFrameSize:newFrame];
-                            }
-                            
-                        }
-                        break;
-                }
-                
-                _actualWindowState = _lastWindowState;
-                WindowEvents->WindowStateChanged(_actualWindowState);
-            }
-            
-            
-            _inSetWindowState = false;
-            
-            return S_OK;
-        }
-    }
-
-    virtual void OnResized () override
-    {
-        if(_shown && !_inSetWindowState && !_transitioningWindowState)
-        {
-            WindowStateChanged();
-        }
-    }
-    
-    virtual bool IsDialog() override
-    {
-        return _isDialog;
-    }
-    
-protected:
-    virtual NSWindowStyleMask GetStyle() override
-    {
-        unsigned long s = NSWindowStyleMaskBorderless;
-
-        switch (_decorations)
-        {
-            case SystemDecorationsNone:
-                s = s | NSWindowStyleMaskFullSizeContentView;
-                break;
-
-            case SystemDecorationsBorderOnly:
-                s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView;
-                break;
-
-            case SystemDecorationsFull:
-                s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless;
-                
-                if(_canResize)
-                {
-                    s = s | NSWindowStyleMaskResizable;
-                }
-                break;
-        }
-
-        if([Window parentWindow] == nullptr)
-        {
-            s |= NSWindowStyleMaskMiniaturizable;
-        }
-        
-        if(_isClientAreaExtended)
-        {
-            s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground;
-        }
-        return s;
-    }
-};
-
-NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
-
-@implementation AutoFitContentView
-{
-    NSVisualEffectView* _titleBarMaterial;
-    NSBox* _titleBarUnderline;
-    NSView* _content;
-    NSVisualEffectView* _blurBehind;
-    double _titleBarHeightHint;
-    bool _settingSize;
-}
-
--(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content
-{
-    _titleBarHeightHint = -1;
-    _content = content;
-    _settingSize = false;
-
-    [self setAutoresizesSubviews:true];
-    [self setWantsLayer:true];
-    
-    _titleBarMaterial = [NSVisualEffectView new];
-    [_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow];
-    [_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar];
-    [_titleBarMaterial setWantsLayer:true];
-    _titleBarMaterial.hidden = true;
-    
-    _titleBarUnderline = [NSBox new];
-    _titleBarUnderline.boxType = NSBoxSeparator;
-    _titleBarUnderline.fillColor = [NSColor underPageBackgroundColor];
-    _titleBarUnderline.hidden = true;
-    
-    [self addSubview:_titleBarMaterial];
-    [self addSubview:_titleBarUnderline];
-    
-    _blurBehind = [NSVisualEffectView new];
-    [_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
-    [_blurBehind setMaterial:NSVisualEffectMaterialLight];
-    [_blurBehind setWantsLayer:true];
-    _blurBehind.hidden = true;
-    
-    [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
-    [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
-    
-    [self addSubview:_blurBehind];
-    [self addSubview:_content];
-    
-    [self setWantsLayer:true];
-    return self;
-}
-
--(void) ShowBlur:(bool)show
-{
-    _blurBehind.hidden = !show;
-}
-
--(void) ShowTitleBar: (bool) show
-{
-    _titleBarMaterial.hidden = !show;
-    _titleBarUnderline.hidden = !show;
-}
-
--(void) SetTitleBarHeightHint: (double) height
-{
-    _titleBarHeightHint = height;
-    
-    [self setFrameSize:self.frame.size];
-}
-
--(void)setFrameSize:(NSSize)newSize
-{
-    if(_settingSize)
-    {
-        return;
-    }
-    
-    _settingSize = true;
-    [super setFrameSize:newSize];
-    
-    auto window = objc_cast<AvnWindow>([self window]);
-    
-    // TODO get actual titlebar size
-    
-    double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint;
-    
-    NSRect tbar;
-    tbar.origin.x = 0;
-    tbar.origin.y = newSize.height - height;
-    tbar.size.width = newSize.width;
-    tbar.size.height = height;
-    
-    [_titleBarMaterial setFrame:tbar];
-    tbar.size.height = height < 1 ? 0 : 1;
-    [_titleBarUnderline setFrame:tbar];
-
-    _settingSize = false;
-}
-
--(void) SetContent: (NSView* _Nonnull) content
-{
-    if(content != nullptr)
-    {
-        [content removeFromSuperview];
-        [self addSubview:content];
-        _content = content;
-    }
-}
-@end
-
-@implementation AvnView
-{
-    ComPtr<WindowBaseImpl> _parent;
-    ComPtr<IUnknown> _swRenderedFrame;
-    AvnFramebuffer _swRenderedFrameBuffer;
-    bool _queuedDisplayFromThread;
-    NSTrackingArea* _area;
-    bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed, _isMouseOver;
-    AvnInputModifiers _modifierState;
-    NSEvent* _lastMouseDownEvent;
-    bool _lastKeyHandled;
-    AvnPixelSize _lastPixelSize;
-    NSObject<IRenderTarget>* _renderTarget;
-    AvnPlatformResizeReason _resizeReason;
-    AvnAccessibilityElement* _accessibilityChild;
-}
-
-- (void)onClosed
-{
-    @synchronized (self)
-    {
-        _parent = nullptr;
-    }
-}
-
--(AvnPixelSize) getPixelSize
-{
-    return _lastPixelSize;
-}
-
-- (NSEvent*) lastMouseDownEvent
-{
-    return _lastMouseDownEvent;
-}
-
-- (void) updateRenderTarget
-{
-    [_renderTarget resize:_lastPixelSize withScale: [[self window] backingScaleFactor]];
-    [self setNeedsDisplayInRect:[self frame]];
-}
-
--(AvnView*)  initWithParent: (WindowBaseImpl*) parent
-{
-    self = [super init];
-    _renderTarget = parent->renderTarget;
-    [self setWantsLayer:YES];
-    [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize];
-    
-    _parent = parent;
-    _area = nullptr;
-    _lastPixelSize.Height = 100;
-    _lastPixelSize.Width = 100;
-    [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]];
-    
-    _modifierState = AvnInputModifiersNone;
-    return self;
-}
-
-- (BOOL)isFlipped
-{
-    return YES;
-}
-
-- (BOOL)wantsUpdateLayer
-{
-    return YES;
-}
-
-- (void)setLayer:(CALayer *)layer
-{
-    [_renderTarget setNewLayer: layer];
-    [super setLayer: layer];
-}
-
-- (BOOL)isOpaque
-{
-    return YES;
-}
-
-- (BOOL)acceptsFirstResponder
-{
-    return true;
-}
-
-- (BOOL)acceptsFirstMouse:(NSEvent *)event
-{
-    return true;
-}
-
-- (BOOL)canBecomeKeyView
-{
-    return true;
-}
-
--(void)setFrameSize:(NSSize)newSize
-{
-    [super setFrameSize:newSize];
-    
-    if(_area != nullptr)
-    {
-        [self removeTrackingArea:_area];
-        _area = nullptr;
-    }
-
-    if (_parent == nullptr)
-    {
-        return;
-    }
-
-    NSRect rect = NSZeroRect;
-    rect.size = newSize;
-    
-    NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingEnabledDuringMouseDrag;
-    _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr];
-    [self addTrackingArea:_area];
-    
-    _parent->UpdateCursor();
-    
-    auto fsize = [self convertSizeToBacking: [self frame].size];
-    
-    if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height)
-    {
-        _lastPixelSize.Width = (int)fsize.width;
-        _lastPixelSize.Height = (int)fsize.height;
-        [self updateRenderTarget];
-    
-        auto reason = [self inLiveResize] ? ResizeUser : _resizeReason;
-        _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason);
-    }
-}
-
-- (void)updateLayer
-{
-    AvnInsidePotentialDeadlock deadlock;
-    if (_parent == nullptr)
-    {
-        return;
-    }
-    
-    _parent->BaseEvents->RunRenderPriorityJobs();
-    
-    if (_parent == nullptr)
-    {
-        return;
-    }
-        
-    _parent->BaseEvents->Paint();
-}
-
-- (void)drawRect:(NSRect)dirtyRect
-{
-    return;
-}
-
--(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose
-{
-    @autoreleasepool {
-        [_renderTarget setSwFrame:fb];
-        dispose->Release();
-    }
-}
-
-- (AvnPoint) translateLocalPoint:(AvnPoint)pt
-{
-    pt.Y = [self bounds].size.height - pt.Y;
-    return pt;
-}
-
-+ (AvnPoint)toAvnPoint:(CGPoint)p
-{
-    AvnPoint result;
-    
-    result.X = p.x;
-    result.Y = p.y;
-    
-    return result;
-}
-
-- (void) viewDidChangeBackingProperties
-{
-    auto fsize = [self convertSizeToBacking: [self frame].size];
-    _lastPixelSize.Width = (int)fsize.width;
-    _lastPixelSize.Height = (int)fsize.height;
-    [self updateRenderTarget];
-    
-    if(_parent != nullptr)
-    {
-        _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
-    }
-    
-    [super viewDidChangeBackingProperties];
-}
-
-- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled
-{
-    auto parentWindow = objc_cast<AvnWindow>([self window]);
-    
-    if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
-    {
-        if(trigerInputWhenDisabled)
-        {
-            auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
-            
-            if(window != nullptr)
-            {
-                window->WindowEvents->GotInputWhenDisabled();
-            }
-        }
-        
-        return TRUE;
-    }
-    
-    return FALSE;
-}
-
-- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
-{
-    bool triggerInputWhenDisabled = type != Move;
-    
-    if([self ignoreUserInput: triggerInputWhenDisabled])
-    {
-        return;
-    }
-    
-    auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
-    auto avnPoint = [AvnView toAvnPoint:localPoint];
-    auto point = [self translateLocalPoint:avnPoint];
-    AvnVector delta;
-    
-    if(type == Wheel)
-    {
-        auto speed = 5;
-        
-        if([event hasPreciseScrollingDeltas])
-        {
-            speed = 50;
-        }
-        
-        delta.X = [event scrollingDeltaX] / speed;
-        delta.Y = [event scrollingDeltaY] / speed;
-        
-        if(delta.X == 0 && delta.Y == 0)
-        {
-            return;
-        }
-    }
-    else if (type == Magnify)
-    {
-        delta.X = delta.Y = [event magnification];
-    }
-    else if (type == Rotate)
-    {
-        delta.X = delta.Y = [event rotation];
-    }
-    else if (type == Swipe)
-    {
-        delta.X = [event deltaX];
-        delta.Y = [event deltaY];
-    }
-    
-    auto timestamp = [event timestamp] * 1000;
-    auto modifiers = [self getModifiers:[event modifierFlags]];
-    
-    if(type != AvnRawMouseEventType::Move ||
-       (
-           [self window] != nil &&
-           (
-                [[self window] firstResponder] == nil
-                || ![[[self window] firstResponder] isKindOfClass: [NSView class]]
-           )
-       )
-    )
-        [self becomeFirstResponder];
-    
-    if(_parent != nullptr)
-    {
-        _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
-    }
-    
-    [super mouseMoved:event];
-}
-
-- (BOOL) resignFirstResponder
-{
-    _parent->BaseEvents->LostFocus();
-    return YES;
-}
-
-- (void)mouseMoved:(NSEvent *)event
-{
-    [self mouseEvent:event withType:Move];
-}
-
-- (void)mouseDown:(NSEvent *)event
-{
-    _isLeftPressed = true;
-    _lastMouseDownEvent = event;
-    [self mouseEvent:event withType:LeftButtonDown];
-}
-
-- (void)otherMouseDown:(NSEvent *)event
-{
-    _lastMouseDownEvent = event;
-
-    switch(event.buttonNumber)
-    {
-        case 2:
-        case 3:
-            _isMiddlePressed = true;
-            [self mouseEvent:event withType:MiddleButtonDown];
-            break;
-        case 4:
-            _isXButton1Pressed = true;
-            [self mouseEvent:event withType:XButton1Down];
-            break;
-        case 5:
-            _isXButton2Pressed = true;
-            [self mouseEvent:event withType:XButton2Down];
-            break;
-    }
-}
-
-- (void)rightMouseDown:(NSEvent *)event
-{
-    _isRightPressed = true;
-    _lastMouseDownEvent = event;
-    [self mouseEvent:event withType:RightButtonDown];
-}
-
-- (void)mouseUp:(NSEvent *)event
-{
-    _isLeftPressed = false;
-    [self mouseEvent:event withType:LeftButtonUp];
-}
-
-- (void)otherMouseUp:(NSEvent *)event
-{
-    switch(event.buttonNumber)
-    {
-        case 2:
-        case 3:
-            _isMiddlePressed = false;
-            [self mouseEvent:event withType:MiddleButtonUp];
-            break;
-        case 4:
-            _isXButton1Pressed = false;
-            [self mouseEvent:event withType:XButton1Up];
-            break;
-        case 5:
-            _isXButton2Pressed = false;
-            [self mouseEvent:event withType:XButton2Up];
-            break;
-    }
-}
-
-- (void)rightMouseUp:(NSEvent *)event
-{
-    _isRightPressed = false;
-    [self mouseEvent:event withType:RightButtonUp];
-}
-
-- (void)mouseDragged:(NSEvent *)event
-{
-    [self mouseEvent:event withType:Move];
-    [super mouseDragged:event];
-}
-
-- (void)otherMouseDragged:(NSEvent *)event
-{
-    [self mouseEvent:event withType:Move];
-    [super otherMouseDragged:event];
-}
-
-- (void)rightMouseDragged:(NSEvent *)event
-{
-    [self mouseEvent:event withType:Move];
-    [super rightMouseDragged:event];
-}
-
-- (void)scrollWheel:(NSEvent *)event
-{
-    [self mouseEvent:event withType:Wheel];
-    [super scrollWheel:event];
-}
-
-- (void)magnifyWithEvent:(NSEvent *)event
-{
-    [self mouseEvent:event withType:Magnify];
-    [super magnifyWithEvent:event];
-}
-
-- (void)rotateWithEvent:(NSEvent *)event
-{
-    [self mouseEvent:event withType:Rotate];
-    [super rotateWithEvent:event];
-}
-
-- (void)swipeWithEvent:(NSEvent *)event
-{
-    [self mouseEvent:event withType:Swipe];
-    [super swipeWithEvent:event];
-}
-
-- (void)mouseEntered:(NSEvent *)event
-{
-    _isMouseOver = true;
-    [super mouseEntered:event];
-}
-
-- (void)mouseExited:(NSEvent *)event
-{
-    _isMouseOver = false;
-    [self mouseEvent:event withType:LeaveWindow];
-    [super mouseExited:event];
-} 
-
-- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
-{
-    if([self ignoreUserInput: false])
-    {
-        return;
-    }
-    
-    auto key = s_KeyMap[[event keyCode]];
-    
-    auto timestamp = [event timestamp] * 1000;
-    auto modifiers = [self getModifiers:[event modifierFlags]];
-     
-    if(_parent != nullptr)
-    {
-        _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
-    }
-}
-
-- (BOOL)performKeyEquivalent:(NSEvent *)event
-{
-    bool result = _lastKeyHandled;
-    
-    _lastKeyHandled = false;
-    
-    return result;
-}
-
-- (void)flagsChanged:(NSEvent *)event
-{
-    auto newModifierState = [self getModifiers:[event modifierFlags]];
-    
-    bool isAltCurrentlyPressed = (_modifierState & Alt) == Alt;
-    bool isControlCurrentlyPressed = (_modifierState & Control) == Control;
-    bool isShiftCurrentlyPressed = (_modifierState & Shift) == Shift;
-    bool isCommandCurrentlyPressed = (_modifierState & Windows) == Windows;
-    
-    bool isAltPressed = (newModifierState & Alt) == Alt;
-    bool isControlPressed = (newModifierState & Control) == Control;
-    bool isShiftPressed = (newModifierState & Shift) == Shift;
-    bool isCommandPressed = (newModifierState & Windows) == Windows;
-    
-    
-    if (isAltPressed && !isAltCurrentlyPressed)
-    {
-        [self keyboardEvent:event withType:KeyDown];
-    }
-    else if (isAltCurrentlyPressed && !isAltPressed)
-    {
-        [self keyboardEvent:event withType:KeyUp];
-    }
-    
-    if (isControlPressed && !isControlCurrentlyPressed)
-    {
-        [self keyboardEvent:event withType:KeyDown];
-    }
-    else if (isControlCurrentlyPressed && !isControlPressed)
-    {
-        [self keyboardEvent:event withType:KeyUp];
-    }
-    
-    if (isShiftPressed && !isShiftCurrentlyPressed)
-    {
-        [self keyboardEvent:event withType:KeyDown];
-    }
-    else if(isShiftCurrentlyPressed && !isShiftPressed)
-    {
-        [self keyboardEvent:event withType:KeyUp];
-    }
-    
-    if(isCommandPressed && !isCommandCurrentlyPressed)
-    {
-        [self keyboardEvent:event withType:KeyDown];
-    }
-    else if(isCommandCurrentlyPressed && ! isCommandPressed)
-    {
-        [self keyboardEvent:event withType:KeyUp];
-    }
-    
-    _modifierState = newModifierState;
-    
-    [[self inputContext] handleEvent:event];
-    [super flagsChanged:event];
-}
-
-- (void)keyDown:(NSEvent *)event
-{
-    [self keyboardEvent:event withType:KeyDown];
-    [[self inputContext] handleEvent:event];
-    [super keyDown:event];
-}
-
-- (void)keyUp:(NSEvent *)event
-{
-    [self keyboardEvent:event withType:KeyUp];
-    [super keyUp:event];
-}
-
-- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod
-{
-    unsigned int rv = 0;
-    
-    if (mod & NSEventModifierFlagControl)
-        rv |= Control;
-    if (mod & NSEventModifierFlagShift)
-        rv |= Shift;
-    if (mod & NSEventModifierFlagOption)
-        rv |= Alt;
-    if (mod & NSEventModifierFlagCommand)
-        rv |= Windows;
-    
-    if (_isLeftPressed)
-        rv |= LeftMouseButton;
-    if (_isMiddlePressed)
-        rv |= MiddleMouseButton;
-    if (_isRightPressed)
-        rv |= RightMouseButton;
-    if (_isXButton1Pressed)
-        rv |= XButton1MouseButton;
-    if (_isXButton2Pressed)
-        rv |= XButton2MouseButton;
-    
-    return (AvnInputModifiers)rv;
-}
-
-- (BOOL)hasMarkedText
-{
-    return _lastKeyHandled;
-}
-
-- (NSRange)markedRange
-{
-    return NSMakeRange(NSNotFound, 0);
-}
-
-- (NSRange)selectedRange
-{
-    return NSMakeRange(NSNotFound, 0);
-}
-
-- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
-{
-    
-}
-
-- (void)unmarkText
-{
-    
-}
-
-- (NSArray<NSString *> *)validAttributesForMarkedText
-{
-    return [NSArray new];
-}
-
-- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
-{
-    return [NSAttributedString new];
-}
-
-- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
-{
-    if(!_lastKeyHandled)
-    {
-        if(_parent != nullptr)
-        {
-            _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
-        }
-    }
-}
-
-- (NSUInteger)characterIndexForPoint:(NSPoint)point
-{
-    return 0;
-}
-
-- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
-{
-    CGRect result;
-    
-    return result;
-}
-
-- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id <NSDraggingInfo>)info
-{
-    auto localPoint = [self convertPoint:[info draggingLocation] toView:self];
-    auto avnPoint = [AvnView toAvnPoint:localPoint];
-    auto point = [self translateLocalPoint:avnPoint];
-    auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]];
-    NSDragOperation nsop = [info draggingSourceOperationMask];
-   
-        auto effects = ConvertDragDropEffects(nsop);
-    int reffects = (int)_parent->BaseEvents
-    ->DragEvent(type, point, modifiers, effects,
-                CreateClipboard([info draggingPasteboard], nil),
-                GetAvnDataObjectHandleFromDraggingInfo(info));
-    
-    NSDragOperation ret = 0;
-    
-    // Ensure that the managed part didn't add any new effects
-    reffects = (int)effects & (int)reffects;
-    
-    // OSX requires exactly one operation
-    if((reffects & (int)AvnDragDropEffects::Copy) != 0)
-        ret = NSDragOperationCopy;
-    else if((reffects & (int)AvnDragDropEffects::Move) != 0)
-        ret = NSDragOperationMove;
-    else if((reffects & (int)AvnDragDropEffects::Link) != 0)
-        ret = NSDragOperationLink;
-    if(ret == 0)
-        ret = NSDragOperationNone;
-    return ret;
-}
-
-- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
-{
-    return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender];
-}
-
-- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
-{
-    return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender];
-}
-
-- (void)draggingExited:(id <NSDraggingInfo>)sender
-{
-    [self triggerAvnDragEvent: AvnDragEventType::Leave info:sender];
-}
-
-- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
-{
-    return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone;
-}
-
-- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
-{
-    return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone;
-}
-
-- (void)concludeDragOperation:(nullable id <NSDraggingInfo>)sender
-{
-    
-}
-
-- (AvnPlatformResizeReason)getResizeReason
-{
-    return _resizeReason;
-}
-
-- (void)setResizeReason:(AvnPlatformResizeReason)reason
-{
-    _resizeReason = reason;
-}
-
-- (AvnAccessibilityElement *) accessibilityChild
-{
-    if (_accessibilityChild == nil)
-    {
-        auto peer = _parent->BaseEvents->GetAutomationPeer();
-        
-        if (peer == nil)
-            return nil;
-
-        _accessibilityChild = [AvnAccessibilityElement acquire:peer];
-    }
-    
-    return _accessibilityChild;
-}
-
-- (NSArray *)accessibilityChildren
-{
-    auto child = [self accessibilityChild];
-    return NSAccessibilityUnignoredChildrenForOnlyChild(child);
-}
-
-- (id)accessibilityHitTest:(NSPoint)point
-{
-    return [[self accessibilityChild] accessibilityHitTest:point];
-}
-
-- (id)accessibilityFocusedUIElement
-{
-    return [[self accessibilityChild] accessibilityFocusedUIElement];
-}
-
-@end
-
-
-@implementation AvnWindow
-{
-    ComPtr<WindowBaseImpl> _parent;
-    bool _canBecomeKeyAndMain;
-    bool _closed;
-    bool _isEnabled;
-    bool _isExtended;
-    AvnMenu* _menu;
-    double _lastScaling;
-    IAvnAutomationPeer* _automationPeer;
-    NSMutableArray* _automationChildren;
-}
-
--(void) setIsExtended:(bool)value;
-{
-    _isExtended = value;
-}
-
--(bool) isDialog
-{
-    return _parent->IsDialog();
-}
-
--(double) getScaling
-{
-    return _lastScaling;
-}
-
--(double) getExtendedTitleBarHeight
-{
-    if(_isExtended)
-    {
-        for (id subview in self.contentView.superview.subviews)
-        {
-            if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")])
-            {
-                NSView *titlebarView = [subview subviews][0];
-
-                return (double)titlebarView.frame.size.height;
-            }
-        }
-
-        return -1;
-    }
-    else
-    {
-        return 0;
-    }
-}
-
-+(void)closeAll
-{
-    [[NSApplication sharedApplication] terminate:self];
-}
-
-- (void)performClose:(id)sender
-{
-    if([[self delegate] respondsToSelector:@selector(windowShouldClose:)])
-    {
-        if(![[self delegate] windowShouldClose:self]) return;
-    }
-    else if([self respondsToSelector:@selector(windowShouldClose:)])
-    {
-        if(![self windowShouldClose:self]) return;
-    }
-
-    [self close];
-}
-
-- (void)pollModalSession:(nonnull NSModalSession)session
-{
-    auto response = [NSApp runModalSession:session];
-    
-    if(response == NSModalResponseContinue)
-    {
-        dispatch_async(dispatch_get_main_queue(), ^{
-            [self pollModalSession:session];
-        });
-    }
-    else if (!_closed)
-    {
-        [self orderOut:self];
-        [NSApp endModalSession:session];
-    }
-}
-
--(void) showWindowMenuWithAppMenu
-{
-    if(_menu != nullptr)
-    {
-        auto appMenuItem = ::GetAppMenuItem();
-        
-        if(appMenuItem != nullptr)
-        {
-            auto appMenu = [appMenuItem menu];
-            
-            [appMenu removeItem:appMenuItem];
-            
-            [_menu insertItem:appMenuItem atIndex:0];
-            
-            [_menu setHasGlobalMenuItem:true];
-        }
-        
-        [NSApp setMenu:_menu];
-    }
-    else
-    {
-        [self showAppMenuOnly];
-    }
-}
-
--(void) showAppMenuOnly
-{
-    auto appMenuItem = ::GetAppMenuItem();
-    
-    if(appMenuItem != nullptr)
-    {
-        auto appMenu = ::GetAppMenu();
-        
-        auto nativeAppMenu = dynamic_cast<AvnAppMenu*>(appMenu);
-        
-        [[appMenuItem menu] removeItem:appMenuItem];
-        
-        if(_menu != nullptr)
-        {
-            [_menu setHasGlobalMenuItem:false];
-        }
-        
-        [nativeAppMenu->GetNative() addItem:appMenuItem];
-        
-        [NSApp setMenu:nativeAppMenu->GetNative()];
-    }
-    else
-    {
-        [NSApp setMenu:nullptr];
-    }
-}
-
--(void) applyMenu:(AvnMenu *)menu
-{
-    if(menu == nullptr)
-    {
-        menu = [AvnMenu new];
-    }
-    
-    _menu = menu;
-}
-
--(void) setCanBecomeKeyAndMain
-{
-    _canBecomeKeyAndMain = true;
-}
-
--(AvnWindow*)  initWithParent: (WindowBaseImpl*) parent
-{
-    self = [super init];
-    [self setReleasedWhenClosed:false];
-    _parent = parent;
-    [self setDelegate:self];
-    _closed = false;
-    _isEnabled = true;
-    
-    _lastScaling = [self backingScaleFactor];
-    [self setOpaque:NO];
-    [self setBackgroundColor: [NSColor clearColor]];
-    _isExtended = false;
-    return self;
-}
-
-- (BOOL)windowShouldClose:(NSWindow *)sender
-{
-    auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
-    
-    if(window != nullptr)
-    {
-        return !window->WindowEvents->Closing();
-    }
-    
-    return true;
-}
-
-- (void)windowDidChangeBackingProperties:(NSNotification *)notification
-{
-    _lastScaling = [self backingScaleFactor];
-}
-
-- (void)windowWillClose:(NSNotification *)notification
-{
-    _closed = true;
-    if(_parent)
-    {
-        ComPtr<WindowBaseImpl> parent = _parent;
-        _parent = NULL;
-        [self restoreParentWindow];
-        parent->BaseEvents->Closed();
-        [parent->View onClosed];
-    }
-}
-
--(BOOL)canBecomeKeyWindow
-{
-    if (_canBecomeKeyAndMain)
-    {
-        // If the window has a child window being shown as a dialog then don't allow it to become the key window.
-        for(NSWindow* uch in [self childWindows])
-        {
-            auto ch = objc_cast<AvnWindow>(uch);
-            if(ch == nil)
-                continue;
-            if (ch.isDialog)
-                return false;
-        }
-        
-        return true;
-    }
-    
-    return false;
-}
-
--(BOOL)canBecomeMainWindow
-{
-    return _canBecomeKeyAndMain;
-}
-
--(bool)shouldTryToHandleEvents
-{
-    return _isEnabled;
-}
-
--(void) setEnabled:(bool)enable
-{
-    _isEnabled = enable;
-}
-
--(void)becomeKeyWindow
-{
-    [self showWindowMenuWithAppMenu];
-    
-    if(_parent != nullptr)
-    {
-        _parent->BaseEvents->Activated();
-    }
-
-    [super becomeKeyWindow];
-}
-
--(void) restoreParentWindow;
-{
-    auto parent = objc_cast<AvnWindow>([self parentWindow]);
-    if(parent != nil)
-    {
-        [parent removeChildWindow:self];
-    }
-}
-
-- (void)windowDidMiniaturize:(NSNotification *)notification
-{
-    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
-    
-    if(parent != nullptr)
-    {
-        parent->WindowStateChanged();
-    }
-}
-
-- (void)windowDidDeminiaturize:(NSNotification *)notification
-{
-    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
-    
-    if(parent != nullptr)
-    {
-        parent->WindowStateChanged();
-    }
-}
-
-- (void)windowDidResize:(NSNotification *)notification
-{
-    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
-    
-    if(parent != nullptr)
-    {
-        parent->WindowStateChanged();
-    }
-}
-
-- (void)windowWillExitFullScreen:(NSNotification *)notification
-{
-    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
-    
-    if(parent != nullptr)
-    {
-        parent->StartStateTransition();
-    }
-}
-
-- (void)windowDidExitFullScreen:(NSNotification *)notification
-{
-    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
-    
-    if(parent != nullptr)
-    {
-        parent->EndStateTransition();
-        
-        if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized)
-        {
-            NSRect screenRect = [[self screen] visibleFrame];
-            [self setFrame:screenRect display:YES];
-        }
-        
-        if(parent->WindowState() == Minimized)
-        {
-            [self miniaturize:nullptr];
-        }
-        
-        parent->WindowStateChanged();
-    }
-}
-
-- (void)windowWillEnterFullScreen:(NSNotification *)notification
-{
-    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
-    
-    if(parent != nullptr)
-    {
-        parent->StartStateTransition();
-    }
-}
-
-- (void)windowDidEnterFullScreen:(NSNotification *)notification
-{
-    auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
-    
-    if(parent != nullptr)
-    {
-        parent->EndStateTransition();
-        parent->WindowStateChanged();
-    }
-}
-
-- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
-{
-    return true;
-}
-
--(void)resignKeyWindow
-{
-    if(_parent)
-        _parent->BaseEvents->Deactivated();
-    
-    [self showAppMenuOnly];
-    
-    [super resignKeyWindow];
-}
-
-- (void)windowDidMove:(NSNotification *)notification
-{
-    AvnPoint position;
-    
-    if(_parent != nullptr)
-    {
-        auto cparent = dynamic_cast<WindowImpl*>(_parent.getRaw());
-        
-        if(cparent != nullptr)
-        {
-            if(cparent->WindowState() == Maximized)
-            {
-                cparent->SetWindowState(Normal);
-            }
-        }
-        
-        _parent->GetPosition(&position);
-        _parent->BaseEvents->PositionChanged(position);
-    }
-}
-
-- (AvnPoint) translateLocalPoint:(AvnPoint)pt
-{
-    pt.Y = [self frame].size.height - pt.Y;
-    return pt;
-}
-
-- (void)sendEvent:(NSEvent *)event
-{
-    [super sendEvent:event];
-    
-    /// This is to detect non-client clicks. This can only be done on Windows... not popups, hence the dynamic_cast.
-    if(_parent != nullptr && dynamic_cast<WindowImpl*>(_parent.getRaw()) != nullptr)
-    {
-        switch(event.type)
-        {
-            case NSEventTypeLeftMouseDown:
-            {
-                AvnView* view = _parent->View;
-                NSPoint windowPoint = [event locationInWindow];
-                NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil];
-                
-                if (!NSPointInRect(viewPoint, view.bounds))
-                {
-                    auto avnPoint = [AvnView toAvnPoint:windowPoint];
-                    auto point = [self translateLocalPoint:avnPoint];
-                    AvnVector delta;
-                   
-                    _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, [event timestamp] * 1000, AvnInputModifiersNone, point, delta);
-                }
-            }
-            break;
-                
-            case NSEventTypeMouseEntered:
-            {
-                _parent->UpdateCursor();
-            }
-            break;
-                
-            case NSEventTypeMouseExited:
-            {
-                [[NSCursor arrowCursor] set];
-            }
-            break;
-                
-            default:
-                break;
-        }
-    }
-}
-
-@end
-
-class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup
-{
-private:
-    BEGIN_INTERFACE_MAP()
-    INHERIT_INTERFACE_MAP(WindowBaseImpl)
-    INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup)
-    END_INTERFACE_MAP()
-    virtual ~PopupImpl(){}
-    ComPtr<IAvnWindowEvents> WindowEvents;
-    PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
-    {
-        WindowEvents = events;
-        [Window setLevel:NSPopUpMenuWindowLevel];
-    }
-protected:
-    virtual NSWindowStyleMask GetStyle() override
-    {
-        return NSWindowStyleMaskBorderless;
-    }
-    
-    virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override
-    {
-        START_COM_CALL;
-        
-        @autoreleasepool
-        {
-            if (Window != nullptr)
-            {
-                [Window setContentSize:NSSize{x, y}];
-            
-                [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];
-            }
-            
-            return S_OK;
-        }
-    }
-public:
-    virtual bool ShouldTakeFocusOnShow() override
-    {
-        return false;
-    }
-};
-
-extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)
-{
-    @autoreleasepool
-    {
-        IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events, gl));
-        return ptr;
-    }
-}
-
-extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
-{
-    @autoreleasepool
-    {
-        IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl);
-        return ptr;
-    }
-}

+ 1 - 0
samples/BindingDemo/BindingDemo.csproj

@@ -5,6 +5,7 @@
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>

+ 0 - 1
samples/ControlCatalog.NetCore/Program.cs

@@ -117,7 +117,6 @@ namespace ControlCatalog.NetCore
                     EnableMultitouch = true
                 })
                 .UseSkia()
-                .UseManagedSystemDialogs()
                 .AfterSetup(builder =>
                 {
                     builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions()

+ 2 - 0
samples/ControlCatalog/ControlCatalog.csproj

@@ -25,6 +25,8 @@
     <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
     <ProjectReference Include="..\SampleControls\ControlSamples.csproj" />
   </ItemGroup>

+ 10 - 0
samples/ControlCatalog/Pages/ComboBoxPage.xaml

@@ -86,6 +86,16 @@
                         <sys:Exception />
                     </DataValidationErrors.Error>
                 </ComboBox>
+
+                <ComboBox PlaceholderText="Scaled" Width="166" RenderTransformOrigin="0,0">
+                    <ComboBox.RenderTransform>
+                      <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
+                    </ComboBox.RenderTransform>
+                    <ComboBoxItem>Inline Items</ComboBoxItem>
+                    <ComboBoxItem>Inline Item 2</ComboBoxItem>
+                    <ComboBoxItem>Inline Item 3</ComboBoxItem>
+                    <ComboBoxItem>Inline Item 4</ComboBoxItem>
+                </ComboBox>
             </WrapPanel>
 
             <CheckBox IsChecked="{Binding WrapSelection}">WrapSelection</CheckBox>

+ 6 - 0
samples/ControlCatalog/Pages/TextBoxPage.xaml

@@ -66,6 +66,12 @@
                  FontFamily="Comic Sans MS"
                  InputMethod.IsInputMethodEnabled="False"
                  Foreground="Red"/>
+        <TextBox AcceptsReturn="True" 
+                 TextWrapping="Wrap" 
+                 Width="200" 
+                 Height="125"
+                 LineHeight="32"
+                 Text="Multiline TextBox with TextWrapping and increased LineHeight.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
       </StackPanel>
       <StackPanel Orientation="Vertical" Spacing="8" Margin="8">
         <Label Classes="h2" Target="{Binding #firstResMFont}">res_m fonts</Label>

+ 1 - 1
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels
         private WindowState _windowState;
         private WindowState[] _windowStates;
         private int _transparencyLevel;
-        private ExtendClientAreaChromeHints _chromeHints;
+        private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome;
         private bool _extendClientAreaEnabled;
         private bool _systemTitleBarEnabled;        
         private bool _preferSystemChromeEnabled;

+ 2 - 1
samples/IntegrationTestApp/IntegrationTestApp.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <OutputType>WinExe</OutputType>
     <TargetFramework>net6.0</TargetFramework>
@@ -18,6 +18,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
   </ItemGroup>
   
   <Import Project="..\..\build\BuildTargets.targets" />

+ 1 - 0
samples/PlatformSanityChecks/PlatformSanityChecks.csproj

@@ -7,6 +7,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
   </ItemGroup>
 

+ 3 - 0
samples/Previewer/Previewer.csproj

@@ -9,6 +9,9 @@
     </Compile>
     <EmbeddedResource Include="**\*.xaml" />
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+  </ItemGroup>
   
   <Import Project="..\..\build\SampleApp.props" />
   <Import Project="..\..\build\ReferenceCoreLibraries.props" />

+ 1 - 0
samples/RenderDemo/RenderDemo.csproj

@@ -12,6 +12,7 @@
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
     <ProjectReference Include="..\SampleControls\ControlSamples.csproj" />

+ 1 - 0
samples/Sandbox/Sandbox.csproj

@@ -10,6 +10,7 @@
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
   </ItemGroup>
   
   <Import Project="..\..\build\SampleApp.props" />

+ 1 - 0
samples/VirtualizationDemo/VirtualizationDemo.csproj

@@ -5,6 +5,7 @@
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>

+ 1 - 0
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@@ -22,6 +22,7 @@
       </EmbeddedResource>
     </ItemGroup>
     <ItemGroup>
+        <ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
         <ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
         <ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
         <ProjectReference Include="..\..\MiniMvvm\MiniMvvm.csproj" />

+ 1 - 0
samples/interop/NativeEmbedSample/NativeEmbedSample.csproj

@@ -9,6 +9,7 @@
 
   <ItemGroup>
     <PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
+    <ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
     <ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
     <ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\..\src\Avalonia.X11\Avalonia.X11.csproj" />

+ 6 - 0
src/Avalonia.Base/Collections/AvaloniaList.cs

@@ -394,7 +394,13 @@ namespace Avalonia.Collections
                         } while (en.MoveNext());
 
                         if (notificationItems is not null)
+                        {
                             NotifyAdd(notificationItems, index);
+                        }
+                        else
+                        {
+                            NotifyCountChanged();
+                        }
                     }
                 }
             }

+ 12 - 7
src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@@ -17,7 +17,7 @@ namespace Avalonia.Data.Core.Plugins
             new Dictionary<(Type, string), PropertyInfo?>();
 
         /// <inheritdoc/>
-        public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null;
+        public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null;
 
         /// <summary>
         /// Starts monitoring the value of a property on an object.
@@ -36,7 +36,7 @@ namespace Avalonia.Data.Core.Plugins
             if (!reference.TryGetTarget(out var instance) || instance is null)
                 return null;
 
-            var p = GetFirstPropertyWithName(instance.GetType(), propertyName);
+            var p = GetFirstPropertyWithName(instance, propertyName);
 
             if (p != null)
             {
@@ -50,8 +50,16 @@ namespace Avalonia.Data.Core.Plugins
             }
         }
 
-        private PropertyInfo? GetFirstPropertyWithName(Type type, string propertyName)
+        private const BindingFlags PropertyBindingFlags =
+            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
+        
+        private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName)
         {
+            if (instance is IReflectableType reflectableType)
+                return reflectableType.GetTypeInfo().GetProperty(propertyName, PropertyBindingFlags);
+
+            var type = instance.GetType();
+            
             var key = (type, propertyName);
 
             if (!_propertyLookup.TryGetValue(key, out var propertyInfo))
@@ -66,10 +74,7 @@ namespace Avalonia.Data.Core.Plugins
         {
             PropertyInfo? found = null;
 
-            const BindingFlags bindingFlags =
-                BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
-
-            var properties = type.GetProperties(bindingFlags);
+            var properties = type.GetProperties(PropertyBindingFlags);
 
             foreach (PropertyInfo propertyInfo in properties)
             {

+ 2 - 3
src/Avalonia.Base/DirectPropertyBase.cs

@@ -2,7 +2,6 @@
 using Avalonia.Data;
 using Avalonia.Reactive;
 using Avalonia.Styling;
-using Avalonia.Utilities;
 
 namespace Avalonia
 {
@@ -188,10 +187,10 @@ namespace Avalonia
             }
             else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType))
             {
-                return new PropertySetterLazyInstance<TValue>(
+                return new PropertySetterTemplateInstance<TValue>(
                     target,
                     this,
-                    () => (TValue)template.Build());
+                    template);
             }
             else
             {

+ 9 - 9
src/Avalonia.Base/Input/InputElement.cs

@@ -94,7 +94,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<KeyEventArgs> KeyDownEvent =
             RoutedEvent.Register<InputElement, KeyEventArgs>(
-                "KeyDown",
+                nameof(KeyDown),
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
 
         /// <summary>
@@ -102,7 +102,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<KeyEventArgs> KeyUpEvent =
             RoutedEvent.Register<InputElement, KeyEventArgs>(
-                "KeyUp",
+                nameof(KeyUp),
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
 
         /// <summary>
@@ -116,7 +116,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<TextInputEventArgs> TextInputEvent =
             RoutedEvent.Register<InputElement, TextInputEventArgs>(
-                "TextInput",
+                nameof(TextInput),
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
         
         /// <summary>
@@ -124,7 +124,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<TextInputMethodClientRequestedEventArgs> TextInputMethodClientRequestedEvent =
             RoutedEvent.Register<InputElement, TextInputMethodClientRequestedEventArgs>(
-                "TextInputMethodClientRequested",
+                nameof(TextInputMethodClientRequested),
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
 
         /// <summary>
@@ -144,7 +144,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<PointerEventArgs> PointerMovedEvent =
             RoutedEvent.Register<InputElement, PointerEventArgs>(
-                "PointerMove",
+                nameof(PointerMoved),
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
 
         /// <summary>
@@ -152,7 +152,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<PointerPressedEventArgs> PointerPressedEvent =
             RoutedEvent.Register<InputElement, PointerPressedEventArgs>(
-                "PointerPressed",
+                nameof(PointerPressed),
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
 
         /// <summary>
@@ -160,7 +160,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<PointerReleasedEventArgs> PointerReleasedEvent =
             RoutedEvent.Register<InputElement, PointerReleasedEventArgs>(
-                "PointerReleased",
+                nameof(PointerReleased),
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
         
         /// <summary>
@@ -168,7 +168,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<PointerCaptureLostEventArgs> PointerCaptureLostEvent =
             RoutedEvent.Register<InputElement, PointerCaptureLostEventArgs>(
-                "PointerCaptureLost", 
+                nameof(PointerCaptureLost), 
                 RoutingStrategies.Direct);
 
         /// <summary>
@@ -176,7 +176,7 @@ namespace Avalonia.Input
         /// </summary>
         public static readonly RoutedEvent<PointerWheelEventArgs> PointerWheelChangedEvent =
             RoutedEvent.Register<InputElement, PointerWheelEventArgs>(
-                "PointerWheelChanged",
+                nameof(PointerWheelChanged),
                 RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
 
         /// <summary>

+ 1 - 1
src/Avalonia.Base/Layout/UniformGridLayout.cs

@@ -116,7 +116,7 @@ namespace Avalonia.Layout
         /// Defines the <see cref="MaximumRowsOrColumnsProperty"/> property.
         /// </summary>
         public static readonly StyledProperty<int> MaximumRowsOrColumnsProperty =
-            AvaloniaProperty.Register<UniformGridLayout, int>(nameof(MinItemWidth));
+            AvaloniaProperty.Register<UniformGridLayout, int>(nameof(MaximumRowsOrColumns));
 
         /// <summary>
         /// Defines the <see cref="Orientation"/> property.

+ 0 - 51
src/Avalonia.Base/Logging/ILogSink.cs

@@ -26,57 +26,6 @@ namespace Avalonia.Logging
             object? source,
             string messageTemplate);
 
-        /// <summary>
-        /// Logs an event.
-        /// </summary>
-        /// <param name="level">The log event level.</param>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValue0">Message property value.</param>
-        void Log<T0>(
-            LogEventLevel level,
-            string area,
-            object? source,
-            string messageTemplate,
-            T0 propertyValue0);
-
-        /// <summary>
-        /// Logs an event.
-        /// </summary>
-        /// <param name="level">The log event level.</param>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValue0">Message property value.</param>
-        /// <param name="propertyValue1">Message property value.</param>
-        void Log<T0, T1>(
-            LogEventLevel level,
-            string area,
-            object? source,
-            string messageTemplate,
-            T0 propertyValue0,
-            T1 propertyValue1);
-
-        /// <summary>
-        /// Logs an event.
-        /// </summary>
-        /// <param name="level">The log event level.</param>
-        /// <param name="area">The area that the event originates.</param>
-        /// <param name="source">The object from which the event originates.</param>
-        /// <param name="messageTemplate">The message template.</param>
-        /// <param name="propertyValue0">Message property value.</param>
-        /// <param name="propertyValue1">Message property value.</param>
-        /// <param name="propertyValue2">Message property value.</param>
-        void Log<T0, T1, T2>(
-            LogEventLevel level,
-            string area,
-            object? source,
-            string messageTemplate,
-            T0 propertyValue0,
-            T1 propertyValue1,
-            T2 propertyValue2);
-
         /// <summary>
         /// Logs a new event.
         /// </summary>

+ 3 - 35
src/Avalonia.Base/Logging/TraceLogSink.cs

@@ -28,31 +28,7 @@ namespace Avalonia.Logging
         {
             if (IsEnabled(level, area))
             {
-                Trace.WriteLine(Format<object, object, object>(area, messageTemplate, source));
-            }
-        }
-
-        public void Log<T0>(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0)
-        {
-            if (IsEnabled(level, area))
-            {
-                Trace.WriteLine(Format<T0, object, object>(area, messageTemplate, source, propertyValue0));
-            }
-        }
-
-        public void Log<T0, T1>(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
-        {
-            if (IsEnabled(level, area))
-            {
-                Trace.WriteLine(Format<T0, T1, object>(area, messageTemplate, source, propertyValue0, propertyValue1));
-            }
-        }
-
-        public void Log<T0, T1, T2>(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
-        {
-            if (IsEnabled(level, area))
-            {
-                Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2));
+                Trace.WriteLine(Format<object, object, object>(area, messageTemplate, source, null));
             }
         }
 
@@ -68,9 +44,7 @@ namespace Avalonia.Logging
             string area,
             string template,
             object? source,
-            T0? v0 = default,
-            T1? v1 = default,
-            T2? v2 = default)
+            object?[]? values)
         {
             var result = new StringBuilder(template.Length);
             var r = new CharacterReader(template.AsSpan());
@@ -93,13 +67,7 @@ namespace Avalonia.Logging
                     if (r.Peek != '{')
                     {
                         result.Append('\'');
-                        result.Append(i++ switch
-                        {
-                            0 => v0,
-                            1 => v1,
-                            2 => v2,
-                            _ => null
-                        });
+                        result.Append(values?[i++]);
                         result.Append('\'');
                         r.TakeUntil('}');
                         r.Take();

+ 2 - 2
src/Avalonia.Base/Media/ConicGradientBrush.cs

@@ -11,7 +11,7 @@ namespace Avalonia.Media
         /// Defines the <see cref="Center"/> property.
         /// </summary>
         public static readonly StyledProperty<RelativePoint> CenterProperty =
-            AvaloniaProperty.Register<RadialGradientBrush, RelativePoint>(
+            AvaloniaProperty.Register<ConicGradientBrush, RelativePoint>(
                 nameof(Center),
                 RelativePoint.Center);
 
@@ -19,7 +19,7 @@ namespace Avalonia.Media
         /// Defines the <see cref="Angle"/> property.
         /// </summary>
         public static readonly StyledProperty<double> AngleProperty =
-            AvaloniaProperty.Register<RadialGradientBrush, double>(
+            AvaloniaProperty.Register<ConicGradientBrush, double>(
                 nameof(Angle),
                 0);
         

+ 3 - 5
src/Avalonia.Base/StyledPropertyBase.cs

@@ -1,9 +1,7 @@
 using System;
-using System.Diagnostics;
 using Avalonia.Data;
 using Avalonia.Reactive;
 using Avalonia.Styling;
-using Avalonia.Utilities;
 
 namespace Avalonia
 {
@@ -12,7 +10,7 @@ namespace Avalonia
     /// </summary>
     public abstract class StyledPropertyBase<TValue> : AvaloniaProperty<TValue>, IStyledPropertyAccessor
     {
-        private bool _inherits;
+        private readonly bool _inherits;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="StyledPropertyBase{T}"/> class.
@@ -243,10 +241,10 @@ namespace Avalonia
             }
             else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType))
             {
-                return new PropertySetterLazyInstance<TValue>(
+                return new PropertySetterTemplateInstance<TValue>(
                     target,
                     this,
-                    () => (TValue)template.Build());
+                    template);
             }
             else
             {

+ 5 - 0
src/Avalonia.Base/Styling/IStyleInstance.cs

@@ -14,6 +14,11 @@ namespace Avalonia.Styling
         /// </summary>
         IStyle Source { get; }
 
+        /// <summary>
+        /// Gets a value indicating whether this style has an activator.
+        /// </summary>
+        bool HasActivator { get; }
+        
         /// <summary>
         /// Gets a value indicating whether this style is active.
         /// </summary>

+ 6 - 4
src/Avalonia.Base/Styling/PropertySetterInstance.cs

@@ -44,7 +44,7 @@ namespace Avalonia.Styling
         {
             if (hasActivator)
             {
-                if (_styledProperty is object)
+                if (_styledProperty is not null)
                 {
                     _subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger);
                 }
@@ -55,13 +55,15 @@ namespace Avalonia.Styling
             }
             else
             {
-                if (_styledProperty is object)
+                var target = (AvaloniaObject) _target;
+                
+                if (_styledProperty is not null)
                 {
-                    _subscription = _target.SetValue(_styledProperty!, _value, BindingPriority.Style);
+                    _subscription = target.SetValue(_styledProperty!, _value, BindingPriority.Style);
                 }
                 else
                 {
-                    _target.SetValue(_directProperty!, _value);
+                    target.SetValue(_directProperty!, _value);
                 }
             }
         }

+ 15 - 16
src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs → src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs

@@ -11,42 +11,42 @@ namespace Avalonia.Styling
     /// evaluated.
     /// </summary>
     /// <typeparam name="T">The target property type.</typeparam>
-    internal class PropertySetterLazyInstance<T> : SingleSubscriberObservableBase<BindingValue<T>>,
+    internal class PropertySetterTemplateInstance<T> : SingleSubscriberObservableBase<BindingValue<T>>,
         ISetterInstance
     {
         private readonly IStyleable _target;
         private readonly StyledPropertyBase<T>? _styledProperty;
         private readonly DirectPropertyBase<T>? _directProperty;
-        private readonly Func<T> _valueFactory;
+        private readonly ITemplate _template;
         private BindingValue<T> _value;
         private IDisposable? _subscription;
         private bool _isActive;
 
-        public PropertySetterLazyInstance(
+        public PropertySetterTemplateInstance(
             IStyleable target,
             StyledPropertyBase<T> property,
-            Func<T> valueFactory)
+            ITemplate template)
         {
             _target = target;
             _styledProperty = property;
-            _valueFactory = valueFactory;
+            _template = template;
         }
 
-        public PropertySetterLazyInstance(
+        public PropertySetterTemplateInstance(
             IStyleable target,
             DirectPropertyBase<T> property,
-            Func<T> valueFactory)
+            ITemplate template)
         {
             _target = target;
             _directProperty = property;
-            _valueFactory = valueFactory;
+            _template = template;
         }
 
         public void Start(bool hasActivator)
         {
             _isActive = !hasActivator;
 
-            if (_styledProperty is object)
+            if (_styledProperty is not null)
             {
                 var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style;
                 _subscription = _target.Bind(_styledProperty, this, priority);
@@ -77,7 +77,7 @@ namespace Avalonia.Styling
 
         public override void Dispose()
         {
-            if (_subscription is object)
+            if (_subscription is not null)
             {
                 var sub = _subscription;
                 _subscription = null;
@@ -85,7 +85,7 @@ namespace Avalonia.Styling
             }
             else if (_isActive)
             {
-                if (_styledProperty is object)
+                if (_styledProperty is not null)
                 {
                     _target.ClearValue(_styledProperty);
                 }
@@ -101,22 +101,21 @@ namespace Avalonia.Styling
         protected override void Subscribed() => PublishNext();
         protected override void Unsubscribed() { }
 
-        private T GetValue()
+        private void EnsureTemplate()
         {
             if (_value.HasValue)
             {
-                return _value.Value;
+                return;
             }
 
-            _value = _valueFactory();
-            return _value.Value;
+            _value = (T) _template.Build();
         }
 
         private void PublishNext()
         {
             if (_isActive)
             {
-                GetValue();
+                EnsureTemplate();
                 PublishNext(_value);
             }
             else

+ 0 - 9
src/Avalonia.Base/Styling/Setter.cs

@@ -1,9 +1,7 @@
 using System;
 using Avalonia.Animation;
 using Avalonia.Data;
-using Avalonia.Data.Core;
 using Avalonia.Metadata;
-using Avalonia.Utilities;
 
 #nullable enable
 
@@ -70,12 +68,5 @@ namespace Avalonia.Styling
 
             return Property.CreateSetterInstance(target, Value);
         }
-
-        private struct SetterVisitorData
-        {
-            public IStyleable target;
-            public object? value;
-            public ISetterInstance? result;
-        }
     }
 }

+ 18 - 17
src/Avalonia.Base/Styling/StyleInstance.cs

@@ -11,10 +11,10 @@ namespace Avalonia.Styling
     /// <summary>
     /// A <see cref="Style"/> which has been instanced on a control.
     /// </summary>
-    internal class StyleInstance : IStyleInstance, IStyleActivatorSink
+    internal sealed class StyleInstance : IStyleInstance, IStyleActivatorSink
     {
-        private readonly List<ISetterInstance>? _setters;
-        private readonly List<IDisposable>? _animations;
+        private readonly ISetterInstance[]? _setters;
+        private readonly IDisposable[]? _animations;
         private readonly IStyleActivator? _activator;
         private readonly Subject<bool>? _animationTrigger;
 
@@ -30,41 +30,42 @@ namespace Avalonia.Styling
             _activator = activator;
             IsActive = _activator is null;
 
-            if (setters is object)
+            if (setters is not null)
             {
                 var setterCount = setters.Count;
 
-                _setters = new List<ISetterInstance>(setterCount);
+                _setters = new ISetterInstance[setterCount];
 
                 for (var i = 0; i < setterCount; ++i)
                 {
-                    _setters.Add(setters[i].Instance(Target));
+                    _setters[i] = setters[i].Instance(Target);
                 }
             }
 
-            if (animations is object && target is Animatable animatable)
+            if (animations is not null && target is Animatable animatable)
             {
                 var animationsCount = animations.Count;
 
-                _animations = new List<IDisposable>(animationsCount);
+                _animations = new IDisposable[animationsCount];
                 _animationTrigger = new Subject<bool>();
 
                 for (var i = 0; i < animationsCount; ++i)
                 {
-                    _animations.Add(animations[i].Apply(animatable, null, _animationTrigger));
+                    _animations[i] = animations[i].Apply(animatable, null, _animationTrigger);
                 }
             }
         }
 
+        public bool HasActivator => _activator is not null;
         public bool IsActive { get; private set; }
         public IStyle Source { get; }
         public IStyleable Target { get; }
 
         public void Start()
         {
-            var hasActivator = _activator is object;
+            var hasActivator = HasActivator;
 
-            if (_setters is object)
+            if (_setters is not null)
             {
                 foreach (var setter in _setters)
                 {
@@ -76,7 +77,7 @@ namespace Avalonia.Styling
             {
                 _activator!.Subscribe(this, 0);
             }
-            else if (_animationTrigger != null)
+            else if (_animationTrigger is not null)
             {
                 _animationTrigger.OnNext(true);
             }
@@ -84,7 +85,7 @@ namespace Avalonia.Styling
 
         public void Dispose()
         {
-            if (_setters is object)
+            if (_setters is not null)
             {
                 foreach (var setter in _setters)
                 {
@@ -92,11 +93,11 @@ namespace Avalonia.Styling
                 }
             }
 
-            if (_animations is object)
+            if (_animations is not null)
             {
-                foreach (var subscripion in _animations)
+                foreach (var subscription in _animations)
                 {
-                    subscripion.Dispose();
+                    subscription.Dispose();
                 }
             }
 
@@ -111,7 +112,7 @@ namespace Avalonia.Styling
 
                 _animationTrigger?.OnNext(value);
 
-                if (_setters is object)
+                if (_setters is not null)
                 {
                     if (IsActive)
                     {

+ 1 - 2
src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs

@@ -5,12 +5,11 @@ namespace Avalonia.Threading
     public class ThreadSafeObjectPool<T> where T : class, new()
     {
         private Stack<T> _stack = new Stack<T>();
-        private object _lock = new object();
         public static ThreadSafeObjectPool<T> Default { get; } = new ThreadSafeObjectPool<T>();
 
         public T Get()
         {
-            lock (_lock)
+            lock (_stack)
             {
                 if(_stack.Count == 0)
                     return new T();

+ 2 - 0
src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs

@@ -218,6 +218,8 @@ namespace Avalonia.Controls.Primitives
             {
                 // No explicit height values were set so we can autosize
                 autoSizeHeight = true;
+                // We need to invalidate desired height in order to grow or shrink as needed
+                InvalidateDesiredHeight();
                 measureHeight = double.PositiveInfinity;
             }
             else

+ 21 - 12
src/Avalonia.Controls/Calendar/CalendarItem.cs

@@ -7,6 +7,7 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Globalization;
+using Avalonia.Collections.Pooled;
 using Avalonia.Controls.Metadata;
 using Avalonia.Data;
 using Avalonia.Input;
@@ -172,13 +173,13 @@ namespace Avalonia.Controls.Primitives
             if (MonthView != null)
             {
                 var childCount = Calendar.RowsPerMonth + Calendar.RowsPerMonth * Calendar.ColumnsPerMonth;
-                var children = new List<IControl>(childCount);
+                using var children = new PooledList<IControl>(childCount);
 
                 for (int i = 0; i < Calendar.RowsPerMonth; i++)
                 {
                     if (_dayTitleTemplate != null)
                     {
-                        var cell = _dayTitleTemplate.Build();
+                        var cell = (Control) _dayTitleTemplate.Build();
                         cell.DataContext = string.Empty;
                         cell.SetValue(Grid.RowProperty, 0);
                         cell.SetValue(Grid.ColumnProperty, i);
@@ -186,11 +187,16 @@ namespace Avalonia.Controls.Primitives
                     }
                 }
 
+                EventHandler<PointerPressedEventArgs> cellMouseLeftButtonDown = Cell_MouseLeftButtonDown;
+                EventHandler<PointerReleasedEventArgs> cellMouseLeftButtonUp = Cell_MouseLeftButtonUp;
+                EventHandler<PointerEventArgs> cellMouseEnter = Cell_MouseEnter;
+                EventHandler<RoutedEventArgs> cellClick = Cell_Click;
+
                 for (int i = 1; i < Calendar.RowsPerMonth; i++)
                 {
                     for (int j = 0; j < Calendar.ColumnsPerMonth; j++)
                     {
-                        CalendarDayButton cell = new CalendarDayButton();
+                        var cell = new CalendarDayButton();
 
                         if (Owner != null)
                         {
@@ -198,10 +204,10 @@ namespace Avalonia.Controls.Primitives
                         }
                         cell.SetValue(Grid.RowProperty, i);
                         cell.SetValue(Grid.ColumnProperty, j);
-                        cell.CalendarDayButtonMouseDown += Cell_MouseLeftButtonDown;
-                        cell.CalendarDayButtonMouseUp += Cell_MouseLeftButtonUp;
-                        cell.PointerEnter += Cell_MouseEnter;
-                        cell.Click += Cell_Click;
+                        cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown;
+                        cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp;
+                        cell.PointerEnter += cellMouseEnter;
+                        cell.Click += cellClick;
                         children.Add(cell);
                     }
                 }
@@ -214,12 +220,15 @@ namespace Avalonia.Controls.Primitives
                 var childCount = Calendar.RowsPerYear * Calendar.ColumnsPerYear;
                 var children = new List<IControl>(childCount);
 
-                CalendarButton month;
+                EventHandler<PointerPressedEventArgs> monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown;
+                EventHandler<PointerReleasedEventArgs> monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp;
+                EventHandler<PointerEventArgs> monthMouseEnter = Month_MouseEnter;
+
                 for (int i = 0; i < Calendar.RowsPerYear; i++)
                 {
                     for (int j = 0; j < Calendar.ColumnsPerYear; j++)
                     {
-                        month = new CalendarButton();
+                        var month = new CalendarButton();
 
                         if (Owner != null)
                         {
@@ -227,9 +236,9 @@ namespace Avalonia.Controls.Primitives
                         }
                         month.SetValue(Grid.RowProperty, i);
                         month.SetValue(Grid.ColumnProperty, j);
-                        month.CalendarLeftMouseButtonDown += Month_CalendarButtonMouseDown;
-                        month.CalendarLeftMouseButtonUp += Month_CalendarButtonMouseUp;
-                        month.PointerEnter += Month_MouseEnter;
+                        month.CalendarLeftMouseButtonDown += monthCalendarButtonMouseDown;
+                        month.CalendarLeftMouseButtonUp += monthCalendarButtonMouseUp;
+                        month.PointerEnter += monthMouseEnter;
                         children.Add(month);
                     }
                 }

+ 43 - 32
src/Avalonia.Controls/Canvas.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Reactive.Concurrency;
 using Avalonia.Input;
 using Avalonia.Layout;
 
@@ -159,47 +160,57 @@ namespace Avalonia.Controls
         }
 
         /// <summary>
-        /// Arranges the control's children.
+        /// Arranges a single child.
         /// </summary>
-        /// <param name="finalSize">The size allocated to the control.</param>
-        /// <returns>The space taken.</returns>
-        protected override Size ArrangeOverride(Size finalSize)
+        /// <param name="child">The child to arrange.</param>
+        /// <param name="finalSize">The size allocated to the canvas.</param>
+        protected virtual void ArrangeChild(Control child, Size finalSize)
         {
-            foreach (Control child in Children)
-            {
-                double x = 0.0;
-                double y = 0.0;
-                double elementLeft = GetLeft(child);
+            double x = 0.0;
+            double y = 0.0;
+            double elementLeft = GetLeft(child);
 
-                if (!double.IsNaN(elementLeft))
-                {
-                    x = elementLeft;
-                }
-                else
+            if (!double.IsNaN(elementLeft))
+            {
+                x = elementLeft;
+            }
+            else
+            {
+                // Arrange with right.
+                double elementRight = GetRight(child);
+                if (!double.IsNaN(elementRight))
                 {
-                    // Arrange with right.
-                    double elementRight = GetRight(child);
-                    if (!double.IsNaN(elementRight))
-                    {
-                        x = finalSize.Width - child.DesiredSize.Width - elementRight;
-                    }
+                    x = finalSize.Width - child.DesiredSize.Width - elementRight;
                 }
+            }
 
-                double elementTop = GetTop(child);
-                if (!double.IsNaN(elementTop) )
-                {
-                    y = elementTop;
-                }
-                else
+            double elementTop = GetTop(child);
+            if (!double.IsNaN(elementTop))
+            {
+                y = elementTop;
+            }
+            else
+            {
+                double elementBottom = GetBottom(child);
+                if (!double.IsNaN(elementBottom))
                 {
-                    double elementBottom = GetBottom(child);
-                    if (!double.IsNaN(elementBottom))
-                    {
-                        y = finalSize.Height - child.DesiredSize.Height - elementBottom;
-                    }
+                    y = finalSize.Height - child.DesiredSize.Height - elementBottom;
                 }
+            }
 
-                child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
+            child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
+        }
+
+        /// <summary>
+        /// Arranges the control's children.
+        /// </summary>
+        /// <param name="finalSize">The size allocated to the control.</param>
+        /// <returns>The space taken.</returns>
+        protected override Size ArrangeOverride(Size finalSize)
+        {
+            foreach (Control child in Children)
+            {
+                ArrangeChild(child, finalSize);
             }
 
             return finalSize;

+ 1 - 1
src/Avalonia.Controls/ContextMenu.cs

@@ -63,7 +63,7 @@ namespace Avalonia.Controls
         /// Defines the <see cref="PlacementRect"/> property.
         /// </summary>
         public static readonly StyledProperty<Rect?> PlacementRectProperty =
-            AvaloniaProperty.Register<Popup, Rect?>(nameof(PlacementRect));
+            Popup.PlacementRectProperty.AddOwner<ContextMenu>();
 
         /// <summary>
         /// Defines the <see cref="WindowManagerAddShadowHint"/> property.

+ 2 - 2
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@@ -38,13 +38,13 @@ namespace Avalonia.Controls
         /// Defines the <see cref="Header"/> property
         /// </summary>
         public static readonly StyledProperty<object> HeaderProperty =
-            AvaloniaProperty.Register<DatePicker, object>(nameof(Header));
+            AvaloniaProperty.Register<TimePicker, object>(nameof(Header));
 
         /// <summary>
         /// Defines the <see cref="HeaderTemplate"/> property
         /// </summary>
         public static readonly StyledProperty<IDataTemplate> HeaderTemplateProperty =
-            AvaloniaProperty.Register<DatePicker, IDataTemplate>(nameof(HeaderTemplate));
+            AvaloniaProperty.Register<TimePicker, IDataTemplate>(nameof(HeaderTemplate));
 
         /// <summary>
         /// Defines the <see cref="ClockIdentifier"/> property

+ 1 - 1
src/Avalonia.Controls/DockPanel.cs

@@ -34,7 +34,7 @@ namespace Avalonia.Controls
         /// </summary>
         public static readonly StyledProperty<bool> LastChildFillProperty =
             AvaloniaProperty.Register<DockPanel, bool>(
-                nameof(LastChildFillProperty),
+                nameof(LastChildFill),
                 defaultValue: true);
 
         /// <summary>

+ 1 - 1
src/Avalonia.Controls/MaskedTextBox.cs

@@ -32,7 +32,7 @@ namespace Avalonia.Controls
              AvaloniaProperty.Register<MaskedTextBox, string?>(nameof(Mask), string.Empty);
 
         public static new readonly StyledProperty<char> PasswordCharProperty =
-             AvaloniaProperty.Register<TextBox, char>(nameof(PasswordChar), '\0');
+             AvaloniaProperty.Register<MaskedTextBox, char>(nameof(PasswordChar), '\0');
 
         public static readonly StyledProperty<char> PromptCharProperty =
              AvaloniaProperty.Register<MaskedTextBox, char>(nameof(PromptChar), '_');

+ 11 - 11
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@@ -52,67 +52,67 @@ namespace Avalonia.Controls.Presenters
         /// <summary>
         /// Defines the <see cref="Foreground"/> property.
         /// </summary>
-        public static readonly AttachedProperty<IBrush?> ForegroundProperty =
+        public static readonly StyledProperty<IBrush?> ForegroundProperty =
             TextElement.ForegroundProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="FontFamily"/> property.
         /// </summary>
-        public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
+        public static readonly StyledProperty<FontFamily> FontFamilyProperty =
             TextElement.FontFamilyProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="FontSize"/> property.
         /// </summary>
-        public static readonly AttachedProperty<double> FontSizeProperty =
+        public static readonly StyledProperty<double> FontSizeProperty =
             TextElement.FontSizeProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="FontStyle"/> property.
         /// </summary>
-        public static readonly AttachedProperty<FontStyle> FontStyleProperty =
+        public static readonly StyledProperty<FontStyle> FontStyleProperty =
             TextElement.FontStyleProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="FontWeight"/> property.
         /// </summary>
-        public static readonly AttachedProperty<FontWeight> FontWeightProperty =
+        public static readonly StyledProperty<FontWeight> FontWeightProperty =
             TextElement.FontWeightProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="FontStretch"/> property.
         /// </summary>
-        public static readonly AttachedProperty<FontStretch> FontStretchProperty =
+        public static readonly StyledProperty<FontStretch> FontStretchProperty =
             TextElement.FontStretchProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="TextAlignment"/> property
         /// </summary>
-        public static readonly AttachedProperty<TextAlignment> TextAlignmentProperty =
+        public static readonly StyledProperty<TextAlignment> TextAlignmentProperty =
             TextBlock.TextAlignmentProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="TextWrapping"/> property
         /// </summary>
-        public static readonly AttachedProperty<TextWrapping> TextWrappingProperty =
+        public static readonly StyledProperty<TextWrapping> TextWrappingProperty =
             TextBlock.TextWrappingProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="TextTrimming"/> property
         /// </summary>
-        public static readonly AttachedProperty<TextTrimming> TextTrimmingProperty =
+        public static readonly StyledProperty<TextTrimming> TextTrimmingProperty =
             TextBlock.TextTrimmingProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="LineHeight"/> property
         /// </summary>
-        public static readonly AttachedProperty<double> LineHeightProperty =
+        public static readonly StyledProperty<double> LineHeightProperty =
             TextBlock.LineHeightProperty.AddOwner<ContentPresenter>();
 
         /// <summary>
         /// Defines the <see cref="MaxLines"/> property
         /// </summary>
-        public static readonly AttachedProperty<int> MaxLinesProperty =
+        public static readonly StyledProperty<int> MaxLinesProperty =
             TextBlock.MaxLinesProperty.AddOwner<ContentPresenter>();
                 
         /// <summary>

+ 23 - 8
src/Avalonia.Controls/Presenters/TextPresenter.cs

@@ -26,14 +26,14 @@ namespace Avalonia.Controls.Presenters
             AvaloniaProperty.Register<TextPresenter, char>(nameof(PasswordChar));
 
         public static readonly StyledProperty<IBrush?> SelectionBrushProperty =
-            AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(SelectionBrushProperty));
+            AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(SelectionBrush));
 
         public static readonly StyledProperty<IBrush?> SelectionForegroundBrushProperty =
-            AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(SelectionForegroundBrushProperty));
+            AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(SelectionForegroundBrush));
 
         public static readonly StyledProperty<IBrush?> CaretBrushProperty =
-            AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(CaretBrushProperty));
-
+            AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(CaretBrush));
+        
         public static readonly DirectProperty<TextPresenter, int> SelectionStartProperty =
             TextBox.SelectionStartProperty.AddOwner<TextPresenter>(
                 o => o.SelectionStart,
@@ -43,7 +43,7 @@ namespace Avalonia.Controls.Presenters
             TextBox.SelectionEndProperty.AddOwner<TextPresenter>(
                 o => o.SelectionEnd,
                 (o, v) => o.SelectionEnd = v);
-
+        
         /// <summary>
         /// Defines the <see cref="Text"/> property.
         /// </summary>
@@ -65,6 +65,12 @@ namespace Avalonia.Controls.Presenters
         public static readonly StyledProperty<TextWrapping> TextWrappingProperty =
             TextBlock.TextWrappingProperty.AddOwner<TextPresenter>();
 
+        /// <summary>
+        /// Defines the <see cref="LineHeight"/> property.
+        /// </summary>
+        public static readonly StyledProperty<double> LineHeightProperty =
+            TextBlock.LineHeightProperty.AddOwner<TextPresenter>();
+        
         /// <summary>
         /// Defines the <see cref="Background"/> property.
         /// </summary>
@@ -179,6 +185,15 @@ namespace Avalonia.Controls.Presenters
             get => GetValue(TextWrappingProperty);
             set => SetValue(TextWrappingProperty, value);
         }
+        
+        /// <summary>
+        /// Gets or sets the line height. By default, this is set to <see cref="double.NaN"/>, which determines the appropriate height automatically.
+        /// </summary>
+        public double LineHeight
+        {
+            get => GetValue(LineHeightProperty);
+            set => SetValue(LineHeightProperty, value);
+        }
 
         /// <summary>
         /// Gets or sets the text alignment.
@@ -253,7 +268,7 @@ namespace Avalonia.Controls.Presenters
             get => GetValue(CaretBrushProperty);
             set => SetValue(CaretBrushProperty, value);
         }
-
+        
         public int SelectionStart
         {
             get
@@ -281,7 +296,7 @@ namespace Avalonia.Controls.Presenters
                 SetAndRaise(SelectionEndProperty, ref _selectionEnd, value);
             }
         }
-
+        
         protected override bool BypassFlowDirectionPolicies => true;
 
         /// <summary>
@@ -301,7 +316,7 @@ namespace Avalonia.Controls.Presenters
             
             var textLayout = new TextLayout(text, typeface, FontSize, foreground, TextAlignment,
                 TextWrapping, maxWidth: maxWidth, maxHeight: maxHeight, textStyleOverrides: textStyleOverrides, 
-                flowDirection: FlowDirection);
+                flowDirection: FlowDirection, lineHeight: LineHeight);
 
             return textLayout;
         }

+ 1 - 1
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@@ -105,7 +105,7 @@ namespace Avalonia.Controls.Primitives
                     }
                     else
                     {
-                        child.Arrange(new Rect(finalSize));
+                        ArrangeChild((Control) child, finalSize);
                     }
                 }
             }

+ 44 - 12
src/Avalonia.Controls/Primitives/IPopupHost.cs

@@ -2,6 +2,7 @@ using System;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives.PopupPositioning;
 using Avalonia.Input;
+using Avalonia.Media;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Controls.Primitives
@@ -17,16 +18,50 @@ namespace Avalonia.Controls.Primitives
     public interface IPopupHost : IDisposable, IFocusScope
     {
         /// <summary>
-        /// Sets the control to display in the popup.
+        /// Gets or sets the fixed width of the popup.
         /// </summary>
-        /// <param name="control"></param>
-        void SetChild(IControl? control);
+        double Width { get; set; }
+
+        /// <summary>
+        /// Gets or sets the minimum width of the popup.
+        /// </summary>
+        double MinWidth { get; set; }
+
+        /// <summary>
+        /// Gets or sets the maximum width of the popup.
+        /// </summary>
+        double MaxWidth { get; set; }
+
+        /// <summary>
+        /// Gets or sets the fixed height of the popup.
+        /// </summary>
+        double Height { get; set; }
+
+        /// <summary>
+        /// Gets or sets the minimum height of the popup.
+        /// </summary>
+        double MinHeight { get; set; }
+
+        /// <summary>
+        /// Gets or sets the maximum height of the popup.
+        /// </summary>
+        double MaxHeight { get; set; }
 
         /// <summary>
         /// Gets the presenter from the control's template.
         /// </summary>
         IContentPresenter? Presenter { get; }
 
+        /// <summary>
+        /// Gets or sets whether the popup appears on top of all other windows.
+        /// </summary>
+        bool Topmost { get; set; }
+
+        /// <summary>
+        /// Gets or sets a transform that will be applied to the popup.
+        /// </summary>
+        Transform? Transform { get; set; }
+
         /// <summary>
         /// Gets the root of the visual tree in the case where the popup is presented using a
         /// separate visual tree.
@@ -57,6 +92,12 @@ namespace Avalonia.Controls.Primitives
             PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All,
             Rect? rect = null);
 
+        /// <summary>
+        /// Sets the control to display in the popup.
+        /// </summary>
+        /// <param name="control"></param>
+        void SetChild(IControl? control);
+
         /// <summary>
         /// Shows the popup.
         /// </summary>
@@ -66,14 +107,5 @@ namespace Avalonia.Controls.Primitives
         /// Hides the popup.
         /// </summary>
         void Hide();
-
-        /// <summary>
-        /// Binds the constraints of the popup host to a set of properties, usally those present on
-        /// <see cref="Popup"/>.
-        /// </summary>
-        IDisposable BindConstraints(AvaloniaObject popup, StyledProperty<double> widthProperty,
-            StyledProperty<double> minWidthProperty, StyledProperty<double> maxWidthProperty,
-            StyledProperty<double> heightProperty, StyledProperty<double> minHeightProperty,
-            StyledProperty<double> maxHeightProperty, StyledProperty<bool> topmostProperty);
     }
 }

+ 19 - 23
src/Avalonia.Controls/Primitives/OverlayPopupHost.cs

@@ -11,6 +11,12 @@ namespace Avalonia.Controls.Primitives
 {
     public class OverlayPopupHost : ContentControl, IPopupHost, IInteractive, IManagedPopupPositionerPopup
     {
+        /// <summary>
+        /// Defines the <see cref="Transform"/> property.
+        /// </summary>
+        public static readonly StyledProperty<Transform?> TransformProperty =
+            PopupRoot.TransformProperty.AddOwner<OverlayPopupHost>();
+
         private readonly OverlayLayer _overlayLayer;
         private PopupPositionerParameters _positionerParameters = new PopupPositionerParameters();
         private ManagedPopupPositioner _positioner;
@@ -29,10 +35,22 @@ namespace Avalonia.Controls.Primitives
         }
 
         public IVisual? HostedVisualTreeRoot => null;
-        
+
+        public Transform? Transform
+        {
+            get => GetValue(TransformProperty);
+            set => SetValue(TransformProperty, value);
+        }
+
         /// <inheritdoc/>
         IInteractive? IInteractive.InteractiveParent => Parent;
 
+        bool IPopupHost.Topmost
+        {
+            get => false;
+            set { /* Not currently supported in overlay popups */ }
+        }
+
         public void Dispose() => Hide();
 
 
@@ -48,28 +66,6 @@ namespace Avalonia.Controls.Primitives
             _shown = false;
         }
 
-        public IDisposable BindConstraints(AvaloniaObject popup, StyledProperty<double> widthProperty, StyledProperty<double> minWidthProperty,
-            StyledProperty<double> maxWidthProperty, StyledProperty<double> heightProperty, StyledProperty<double> minHeightProperty,
-            StyledProperty<double> maxHeightProperty, StyledProperty<bool> topmostProperty)
-        {
-            // Topmost property is not supported
-            var bindings = new List<IDisposable>();
-
-            void Bind(AvaloniaProperty what, AvaloniaProperty to) => bindings.Add(this.Bind(what, popup[~to]));
-            Bind(WidthProperty, widthProperty);
-            Bind(MinWidthProperty, minWidthProperty);
-            Bind(MaxWidthProperty, maxWidthProperty);
-            Bind(HeightProperty, heightProperty);
-            Bind(MinHeightProperty, minHeightProperty);
-            Bind(MaxHeightProperty, maxHeightProperty);
-            
-            return Disposable.Create(() =>
-            {
-                foreach (var x in bindings)
-                    x.Dispose();
-            });
-        }
-
         public void ConfigurePosition(IVisual target, PlacementMode placement, Point offset,
             PopupAnchor anchor = PopupAnchor.None, PopupGravity gravity = PopupGravity.None,
             PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All,

+ 135 - 17
src/Avalonia.Controls/Primitives/Popup.cs

@@ -14,6 +14,8 @@ using Avalonia.LogicalTree;
 using Avalonia.Metadata;
 using Avalonia.Platform;
 using Avalonia.VisualTree;
+using Avalonia.Media;
+using Avalonia.Utilities;
 
 namespace Avalonia.Controls.Primitives
 {
@@ -25,7 +27,7 @@ namespace Avalonia.Controls.Primitives
 #pragma warning restore CS0612 // Type or member is obsolete
     {
         public static readonly StyledProperty<bool> WindowManagerAddShadowHintProperty =
-            AvaloniaProperty.Register<PopupRoot, bool>(nameof(WindowManagerAddShadowHint), false);
+            AvaloniaProperty.Register<Popup, bool>(nameof(WindowManagerAddShadowHint), false);
 
         /// <summary>
         /// Defines the <see cref="Child"/> property.
@@ -33,6 +35,12 @@ namespace Avalonia.Controls.Primitives
         public static readonly StyledProperty<Control?> ChildProperty =
             AvaloniaProperty.Register<Popup, Control?>(nameof(Child));
 
+        /// <summary>
+        /// Defines the <see cref="InheritsTransform"/> property.
+        /// </summary>
+        public static readonly StyledProperty<bool> InheritsTransformProperty =
+            AvaloniaProperty.Register<Popup, bool>(nameof(InheritsTransform));
+
         /// <summary>
         /// Defines the <see cref="IsOpen"/> property.
         /// </summary>
@@ -196,6 +204,16 @@ namespace Avalonia.Controls.Primitives
             set;
         }
 
+        /// <summary>
+        /// Gets or sets a value that determines whether the popup inherits the render transform
+        /// from its <see cref="PlacementTarget"/>. Defaults to false.
+        /// </summary>
+        public bool InheritsTransform
+        {
+            get => GetValue(InheritsTransformProperty);
+            set => SetValue(InheritsTransformProperty, value);
+        }
+
         /// <summary>
         /// Gets or sets a value that determines how the <see cref="Popup"/> can be dismissed.
         /// </summary>
@@ -395,24 +413,29 @@ namespace Avalonia.Controls.Primitives
             }
 
             _isOpenRequested = false;
-            var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
 
+            var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
             var handlerCleanup = new CompositeDisposable(7);
 
-            popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty,
-                HeightProperty, MinHeightProperty, MaxHeightProperty, TopmostProperty).DisposeWith(handlerCleanup);
-
+            UpdateHostSizing(popupHost, topLevel, placementTarget);
+            popupHost.Topmost = Topmost;
             popupHost.SetChild(Child);
             ((ISetLogicalParent)popupHost).SetParent(this);
 
-            popupHost.ConfigurePosition(
-                placementTarget,
-                PlacementMode,
-                new Point(HorizontalOffset, VerticalOffset),
-                PlacementAnchor,
-                PlacementGravity,
-                PlacementConstraintAdjustment,
-                PlacementRect);
+            if (InheritsTransform && placementTarget is Control c)
+            {
+                SubscribeToEventHandler<Control, EventHandler<AvaloniaPropertyChangedEventArgs>>(
+                    c,
+                    PlacementTargetPropertyChanged,
+                    (x, handler) => x.PropertyChanged += handler,
+                    (x, handler) => x.PropertyChanged -= handler).DisposeWith(handlerCleanup);
+            }
+            else
+            {
+                popupHost.Transform = null;
+            }
+
+            UpdateHostPosition(popupHost, placementTarget);
 
             SubscribeToEventHandler<IPopupHost, EventHandler<TemplateAppliedEventArgs>>(popupHost, RootTemplateApplied,
                 (x, handler) => x.TemplateApplied += handler,
@@ -494,7 +517,7 @@ namespace Avalonia.Controls.Primitives
                 }
             }
 
-            _openState = new PopupOpenState(topLevel, popupHost, cleanupPopup);
+            _openState = new PopupOpenState(placementTarget, topLevel, popupHost, cleanupPopup);
 
             WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint);
 
@@ -542,7 +565,93 @@ namespace Avalonia.Controls.Primitives
             base.OnDetachedFromLogicalTree(e);
             Close();
         }
-        
+
+        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+        {
+            if (_openState is not null)
+            {
+                if (change.Property == WidthProperty ||
+                    change.Property == MinWidthProperty ||
+                    change.Property == MaxWidthProperty ||
+                    change.Property == HeightProperty ||
+                    change.Property == MinHeightProperty ||
+                    change.Property == MaxHeightProperty)
+                {
+                    UpdateHostSizing(_openState.PopupHost, _openState.TopLevel, _openState.PlacementTarget);
+                }
+                else if (change.Property == PlacementTargetProperty ||
+                         change.Property == PlacementModeProperty ||
+                         change.Property == HorizontalOffsetProperty ||
+                         change.Property == VerticalOffsetProperty ||
+                         change.Property == PlacementAnchorProperty ||
+                         change.Property == PlacementConstraintAdjustmentProperty ||
+                         change.Property == PlacementRectProperty)
+                {
+                    if (change.Property == PlacementTargetProperty)
+                    {
+                        var newTarget = change.GetNewValue<Control?>() ?? this.FindLogicalAncestorOfType<IControl>();
+
+                        if (newTarget is null || newTarget.GetVisualRoot() != _openState.TopLevel)
+                        {
+                            Close();
+                            return;
+                        }
+
+                        _openState.PlacementTarget = newTarget;
+                    }
+
+                    UpdateHostPosition(_openState.PopupHost, _openState.PlacementTarget);
+                }
+                else if (change.Property == TopmostProperty)
+                {
+                    _openState.PopupHost.Topmost = change.GetNewValue<bool>();
+                }
+            }
+        }
+
+        private void UpdateHostPosition(IPopupHost popupHost, IControl placementTarget)
+        {
+            popupHost.ConfigurePosition(
+                placementTarget,
+                PlacementMode,
+                new Point(HorizontalOffset, VerticalOffset),
+                PlacementAnchor,
+                PlacementGravity,
+                PlacementConstraintAdjustment,
+                PlacementRect ?? new Rect(default, placementTarget.Bounds.Size));
+        }
+
+        private void UpdateHostSizing(IPopupHost popupHost, TopLevel topLevel, IControl placementTarget)
+        {
+            var scaleX = 1.0;
+            var scaleY = 1.0;
+
+            if (InheritsTransform && placementTarget.TransformToVisual(topLevel) is Matrix m)
+            {
+                scaleX = Math.Sqrt(m.M11 * m.M11 + m.M12 * m.M12);
+                scaleY = Math.Sqrt(m.M11 * m.M11 + m.M12 * m.M12);
+
+                // Ideally we'd only assign a ScaleTransform here when the scale != 1, but there's
+                // an issue with LayoutTransformControl in that it sets its LayoutTransform property
+                // with LocalValue priority in ArrangeOverride in certain cases when LayoutTransform
+                // is null, which breaks TemplateBindings to this property. Offending commit/line:
+                //
+                // https://github.com/AvaloniaUI/Avalonia/commit/6fbe1c2180ef45a940e193f1b4637e64eaab80ed#diff-5344e793df13f462126a8153ef46c44194f244b6890f25501709bae51df97f82R54
+                popupHost.Transform = new ScaleTransform(scaleX, scaleY);
+            }
+            else
+            {
+                popupHost.Transform = null;
+            }
+
+            popupHost.Width = Width * scaleX;
+            popupHost.MinWidth = MinWidth * scaleX;
+            popupHost.MaxWidth = MaxWidth * scaleX;
+            popupHost.Height = Height * scaleY;
+            popupHost.MinHeight = MinHeight * scaleY;
+            popupHost.MaxHeight = MaxHeight * scaleY;
+        }
+
         private void HandlePositionChange()
         {
             if (_openState != null)
@@ -824,6 +933,14 @@ namespace Avalonia.Controls.Primitives
             }
         }
         
+        private void PlacementTargetPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+        {
+            if (_openState is not null && e.Property == Visual.TransformedBoundsProperty)
+            {
+                UpdateHostSizing(_openState.PopupHost, _openState.TopLevel, _openState.PlacementTarget);
+            }
+        }
+
         private void WindowLostFocus()
         {
             if (IsLightDismissEnabled)
@@ -862,15 +979,16 @@ namespace Avalonia.Controls.Primitives
             private readonly IDisposable _cleanup;
             private IDisposable? _presenterCleanup;
 
-            public PopupOpenState(TopLevel topLevel, IPopupHost popupHost, IDisposable cleanup)
+            public PopupOpenState(IControl placementTarget, TopLevel topLevel, IPopupHost popupHost, IDisposable cleanup)
             {
+                PlacementTarget = placementTarget;
                 TopLevel = topLevel;
                 PopupHost = popupHost;
                 _cleanup = cleanup;
             }
 
             public TopLevel TopLevel { get; }
-
+            public IControl PlacementTarget { get; set; }
             public IPopupHost PopupHost { get; }
 
             public void SetPresenterSubscription(IDisposable? presenterCleanup)

+ 15 - 24
src/Avalonia.Controls/Primitives/PopupRoot.cs

@@ -1,6 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Reactive.Disposables;
 using Avalonia.Automation.Peers;
 using Avalonia.Controls.Primitives.PopupPositioning;
 using Avalonia.Interactivity;
@@ -8,7 +6,6 @@ using Avalonia.Media;
 using Avalonia.Platform;
 using Avalonia.Styling;
 using Avalonia.VisualTree;
-using JetBrains.Annotations;
 
 namespace Avalonia.Controls.Primitives
 {
@@ -17,6 +14,12 @@ namespace Avalonia.Controls.Primitives
     /// </summary>
     public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost
     {
+        /// <summary>
+        /// Defines the <see cref="Transform"/> property.
+        /// </summary>
+        public static readonly StyledProperty<Transform?> TransformProperty =
+            AvaloniaProperty.Register<PopupRoot, Transform?>(nameof(Transform));
+
         private PopupPositionerParameters _positionerParameters;        
 
         /// <summary>
@@ -54,6 +57,15 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         public new IPopupImpl? PlatformImpl => (IPopupImpl?)base.PlatformImpl;               
 
+        /// <summary>
+        /// Gets or sets a transform that will be applied to the popup.
+        /// </summary>
+        public Transform? Transform
+        {
+            get => GetValue(TransformProperty);
+            set => SetValue(TransformProperty, value);
+        }
+
         /// <summary>
         /// Gets the parent control in the event route.
         /// </summary>
@@ -103,27 +115,6 @@ namespace Avalonia.Controls.Primitives
 
         IVisual IPopupHost.HostedVisualTreeRoot => this;
         
-        public IDisposable BindConstraints(AvaloniaObject popup, StyledProperty<double> widthProperty, StyledProperty<double> minWidthProperty,
-            StyledProperty<double> maxWidthProperty, StyledProperty<double> heightProperty, StyledProperty<double> minHeightProperty,
-            StyledProperty<double> maxHeightProperty, StyledProperty<bool> topmostProperty)
-        {
-            var bindings = new List<IDisposable>();
-
-            void Bind(AvaloniaProperty what, AvaloniaProperty to) => bindings.Add(this.Bind(what, popup[~to]));
-            Bind(WidthProperty, widthProperty);
-            Bind(MinWidthProperty, minWidthProperty);
-            Bind(MaxWidthProperty, maxWidthProperty);
-            Bind(HeightProperty, heightProperty);
-            Bind(MinHeightProperty, minHeightProperty);
-            Bind(MaxHeightProperty, maxHeightProperty);
-            Bind(TopmostProperty, topmostProperty);
-            return Disposable.Create(() =>
-            {
-                foreach (var x in bindings)
-                    x.Dispose();
-            });
-        }
-
         protected override Size MeasureOverride(Size availableSize)
         {
             var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity;

+ 3 - 3
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -94,7 +94,7 @@ namespace Avalonia.Controls.Primitives
         /// Defines the <see cref="IsTextSearchEnabled"/> property.
         /// </summary>
         public static readonly StyledProperty<bool> IsTextSearchEnabledProperty =
-            AvaloniaProperty.Register<ItemsControl, bool>(nameof(IsTextSearchEnabled), false);
+            AvaloniaProperty.Register<SelectingItemsControl, bool>(nameof(IsTextSearchEnabled), false);
 
         /// <summary>
         /// Event that should be raised by items that implement <see cref="ISelectable"/> to
@@ -111,14 +111,14 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
             RoutedEvent.Register<SelectingItemsControl, SelectionChangedEventArgs>(
-                "SelectionChanged",
+                nameof(SelectionChanged),
                 RoutingStrategies.Bubble);
 
         /// <summary>
         /// Defines the <see cref="WrapSelection"/> property.
         /// </summary>
         public static readonly StyledProperty<bool> WrapSelectionProperty =
-            AvaloniaProperty.Register<ItemsControl, bool>(nameof(WrapSelection), defaultValue: false);
+            AvaloniaProperty.Register<SelectingItemsControl, bool>(nameof(WrapSelection), defaultValue: false);
 
         private static readonly IList Empty = Array.Empty<object>();
         private string _textSearchTerm = string.Empty;

+ 1 - 1
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@@ -97,7 +97,7 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         public static readonly RoutedEvent<TemplateAppliedEventArgs> TemplateAppliedEvent =
             RoutedEvent.Register<TemplatedControl, TemplateAppliedEventArgs>(
-                "TemplateApplied", 
+                nameof(TemplateApplied), 
                 RoutingStrategies.Direct);
 
         private IControlTemplate? _appliedTemplate;

+ 3 - 3
src/Avalonia.Controls/Primitives/Track.cs

@@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives
             AvaloniaProperty.Register<Track, bool>(nameof(IsDirectionReversed));
 
         public static readonly StyledProperty<bool> IgnoreThumbDragProperty =
-            AvaloniaProperty.Register<Track, bool>(nameof(IsThumbDragHandled));
+            AvaloniaProperty.Register<Track, bool>(nameof(IgnoreThumbDrag));
 
         private double _minimum;
         private double _maximum = 100.0;
@@ -118,7 +118,7 @@ namespace Avalonia.Controls.Primitives
             set { SetValue(IsDirectionReversedProperty, value); }
         }
 
-        public bool IsThumbDragHandled
+        public bool IgnoreThumbDrag
         {
             get { return GetValue(IgnoreThumbDragProperty); }
             set { SetValue(IgnoreThumbDragProperty, value); }
@@ -442,7 +442,7 @@ namespace Avalonia.Controls.Primitives
 
         private void ThumbDragged(object? sender, VectorEventArgs e)
         {
-            if (IsThumbDragHandled)
+            if (IgnoreThumbDrag)
                 return;
                 
             Value = MathUtilities.Clamp(

+ 2 - 2
src/Avalonia.Controls/Slider.cs

@@ -76,7 +76,7 @@ namespace Avalonia.Controls
         /// Defines the <see cref="TickPlacement"/> property.
         /// </summary>
         public static readonly StyledProperty<TickPlacement> TickPlacementProperty =
-            AvaloniaProperty.Register<TickBar, TickPlacement>(nameof(TickPlacement), 0d);
+            AvaloniaProperty.Register<Slider, TickPlacement>(nameof(TickPlacement), 0d);
 
         /// <summary>
         /// Defines the <see cref="TicksProperty"/> property.
@@ -197,7 +197,7 @@ namespace Avalonia.Controls
 
             if (_track != null)
             {
-                _track.IsThumbDragHandled = true;
+                _track.IgnoreThumbDrag = true;
             }
 
             if (_decreaseButton != null)

+ 1 - 1
src/Avalonia.Controls/SplitView.cs

@@ -138,7 +138,7 @@ namespace Avalonia.Controls
         /// Defines the <see cref="PaneTemplate"/> property.
         /// </summary>
         public static readonly StyledProperty<IDataTemplate> PaneTemplateProperty =
-            AvaloniaProperty.Register<HeaderedContentControl, IDataTemplate>(nameof(PaneTemplate));
+            AvaloniaProperty.Register<SplitView, IDataTemplate>(nameof(PaneTemplate));
 
         /// <summary>
         /// Defines the <see cref="UseLightDismissOverlayMode"/> property

+ 23 - 8
src/Avalonia.Controls/TextBox.cs

@@ -55,13 +55,13 @@ namespace Avalonia.Controls
             AvaloniaProperty.Register<TextBox, char>(nameof(PasswordChar));
 
         public static readonly StyledProperty<IBrush?> SelectionBrushProperty =
-            AvaloniaProperty.Register<TextBox, IBrush?>(nameof(SelectionBrushProperty));
+            AvaloniaProperty.Register<TextBox, IBrush?>(nameof(SelectionBrush));
 
         public static readonly StyledProperty<IBrush?> SelectionForegroundBrushProperty =
-            AvaloniaProperty.Register<TextBox, IBrush?>(nameof(SelectionForegroundBrushProperty));
+            AvaloniaProperty.Register<TextBox, IBrush?>(nameof(SelectionForegroundBrush));
 
         public static readonly StyledProperty<IBrush?> CaretBrushProperty =
-            AvaloniaProperty.Register<TextBox, IBrush?>(nameof(CaretBrushProperty));
+            AvaloniaProperty.Register<TextBox, IBrush?>(nameof(CaretBrush));
 
         public static readonly DirectProperty<TextBox, int> SelectionStartProperty =
             AvaloniaProperty.RegisterDirect<TextBox, int>(
@@ -79,8 +79,8 @@ namespace Avalonia.Controls
             AvaloniaProperty.Register<TextBox, int>(nameof(MaxLength), defaultValue: 0);
 
         public static readonly StyledProperty<int> MaxLinesProperty =
-      AvaloniaProperty.Register<TextBox, int>(nameof(MaxLines), defaultValue: 0);
-
+            AvaloniaProperty.Register<TextBox, int>(nameof(MaxLines), defaultValue: 0);
+        
         public static readonly DirectProperty<TextBox, string?> TextProperty =
             TextBlock.TextProperty.AddOwnerWithDataValidation<TextBox>(
                 o => o.Text,
@@ -105,6 +105,12 @@ namespace Avalonia.Controls
 
         public static readonly StyledProperty<TextWrapping> TextWrappingProperty =
             TextBlock.TextWrappingProperty.AddOwner<TextBox>();
+        
+        /// <summary>
+        /// Defines see <see cref="TextPresenter.LineHeight"/> property.
+        /// </summary>
+        public static readonly StyledProperty<double> LineHeightProperty =
+            TextBlock.LineHeightProperty.AddOwner<TextBox>();
 
         public static readonly StyledProperty<string?> WatermarkProperty =
             AvaloniaProperty.Register<TextBox, string?>(nameof(Watermark));
@@ -154,15 +160,15 @@ namespace Avalonia.Controls
 
         public static readonly RoutedEvent<RoutedEventArgs> CopyingToClipboardEvent =
             RoutedEvent.Register<TextBox, RoutedEventArgs>(
-                "CopyingToClipboard", RoutingStrategies.Bubble);
+                nameof(CopyingToClipboard), RoutingStrategies.Bubble);
 
         public static readonly RoutedEvent<RoutedEventArgs> CuttingToClipboardEvent =
             RoutedEvent.Register<TextBox, RoutedEventArgs>(
-                "CuttingToClipboard", RoutingStrategies.Bubble);
+                nameof(CuttingToClipboard), RoutingStrategies.Bubble);
 
         public static readonly RoutedEvent<RoutedEventArgs> PastingFromClipboardEvent =
             RoutedEvent.Register<TextBox, RoutedEventArgs>(
-                "PastingFromClipboard", RoutingStrategies.Bubble);
+                nameof(PastingFromClipboard), RoutingStrategies.Bubble);
 
         readonly struct UndoRedoState : IEquatable<UndoRedoState>
         {
@@ -358,6 +364,15 @@ namespace Avalonia.Controls
             get { return GetValue(MaxLinesProperty); }
             set { SetValue(MaxLinesProperty, value); }
         }
+        
+        /// <summary>
+        /// Gets or sets the line height.
+        /// </summary>
+        public double LineHeight
+        {
+            get { return GetValue(LineHeightProperty); }
+            set { SetValue(LineHeightProperty, value); }
+        }
 
         [Content]
         public string? Text

+ 48 - 5
src/Avalonia.Controls/Viewbox.cs

@@ -1,4 +1,5 @@
 using Avalonia.Media;
+using Avalonia.Media.Immutable;
 using Avalonia.Metadata;
 
 namespace Avalonia.Controls
@@ -8,7 +9,7 @@ namespace Avalonia.Controls
     /// </summary>
     public class Viewbox : Control
     {
-        private Decorator _containerVisual;
+        private readonly ViewboxContainer _containerVisual;
 
         /// <summary>
         /// Defines the <see cref="Stretch"/> property.
@@ -37,9 +38,10 @@ namespace Avalonia.Controls
 
         public Viewbox()
         {
-            _containerVisual = new Decorator();
+            // The Child control is hosted inside a ViewboxContainer control so that the transform
+            // can be applied independently of the Viewbox and Child transforms.
+            _containerVisual = new ViewboxContainer();
             _containerVisual.RenderTransformOrigin = RelativePoint.TopLeft;
-            LogicalChildren.Add(_containerVisual);
             VisualChildren.Add(_containerVisual);
         }
 
@@ -88,7 +90,22 @@ namespace Avalonia.Controls
 
             if (change.Property == ChildProperty)
             {
-                _containerVisual.Child = change.GetNewValue<IControl>();
+                var (oldChild, newChild) = change.GetOldAndNewValue<IControl>();
+
+                if (oldChild is not null)
+                {
+                    ((ISetLogicalParent)oldChild).SetParent(null);
+                    LogicalChildren.Remove(oldChild);
+                }
+
+                _containerVisual.Child = newChild;
+
+                if (newChild is not null)
+                {
+                    ((ISetLogicalParent)newChild).SetParent(this);
+                    LogicalChildren.Add(newChild);
+                }
+
                 InvalidateMeasure();
             }
         }
@@ -120,7 +137,7 @@ namespace Avalonia.Controls
                 var childSize = child.DesiredSize;
                 var scale = Stretch.CalculateScaling(finalSize, childSize, StretchDirection);
 
-                InternalTransform = new ScaleTransform(scale.X, scale.Y);
+                InternalTransform = new ImmutableTransform(Matrix.CreateScale(scale.X, scale.Y));
 
                 child.Arrange(new Rect(childSize));
 
@@ -129,5 +146,31 @@ namespace Avalonia.Controls
 
             return finalSize;
         }
+
+        /// <summary>
+        /// A simple container control which hosts its child as a visual but not logical child.
+        /// </summary>
+        private class ViewboxContainer : Control
+        {
+            private IControl? _child;
+
+            public IControl? Child
+            {
+                get => _child;
+                set
+                {
+                    if (_child != value)
+                    {
+                        if (_child is not null)
+                            VisualChildren.Remove(_child);
+
+                        _child = value;
+
+                        if (_child is not null)
+                            VisualChildren.Add(_child);
+                    }
+                }
+            }
+        }
     }
 }

+ 2 - 2
src/Avalonia.Controls/Window.cs

@@ -171,13 +171,13 @@ namespace Avalonia.Controls
         /// <summary>
         /// Routed event that can be used for global tracking of window destruction
         /// </summary>
-        public static readonly RoutedEvent WindowClosedEvent =
+        public static readonly RoutedEvent<RoutedEventArgs> WindowClosedEvent =
             RoutedEvent.Register<Window, RoutedEventArgs>("WindowClosed", RoutingStrategies.Direct);
 
         /// <summary>
         /// Routed event that can be used for global tracking of opening windows
         /// </summary>
-        public static readonly RoutedEvent WindowOpenedEvent =
+        public static readonly RoutedEvent<RoutedEventArgs> WindowOpenedEvent =
             RoutedEvent.Register<Window, RoutedEventArgs>("WindowOpened", RoutingStrategies.Direct);
 
 

+ 0 - 1
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@@ -18,7 +18,6 @@
     <ProjectReference Include="..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
     <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
     <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
-    <ProjectReference Include="..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
   </ItemGroup>
   <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\ApiDiff.props" />

+ 2 - 1
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs

@@ -60,7 +60,8 @@ namespace Avalonia.Diagnostics.ViewModels
 
                 var styleDiagnostics = styledElement.GetStyleDiagnostics();
 
-                foreach (var appliedStyle in styleDiagnostics.AppliedStyles)
+                // We need to place styles without activator first, such styles will be overwritten by ones with activators.
+                foreach (var appliedStyle in styleDiagnostics.AppliedStyles.OrderBy(s => s.HasActivator))
                 {
                     var styleSource = appliedStyle.Source;
 

+ 10 - 3
src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs

@@ -36,6 +36,13 @@ namespace Avalonia.FreeDesktop
 
         private string GetSymlinkTarget(string x) => Path.GetFullPath(Path.Combine(DevByLabelDir, NativeMethods.ReadLink(x)));
 
+        private string UnescapeString(string input, string regexText, int escapeBase) =>
+            new Regex(regexText).Replace(input, m => Convert.ToChar(Convert.ToByte(m.Groups[1].Value, escapeBase)).ToString());
+
+        private string UnescapePathFromProcMounts(string input) => UnescapeString(input, @"\\(\d{3})", 8);
+
+        private string UnescapeDeviceLabel(string input) => UnescapeString(input, @"\\x([0-9a-f]{2})", 16);
+
         private void Poll(long _)
         {
             var fProcPartitions = File.ReadAllLines(ProcPartitionsDir)
@@ -47,14 +54,14 @@ namespace Avalonia.FreeDesktop
 
             var fProcMounts = File.ReadAllLines(ProcMountsDir)
                                   .Select(x => x.Split(' '))
-                                  .Select(x => (x[0], x[1]))
+                                  .Select(x => (x[0], UnescapePathFromProcMounts(x[1])))
                                   .Where(x => !x.Item2.StartsWith("/snap/", StringComparison.InvariantCultureIgnoreCase));
 
             var labelDirEnum = Directory.Exists(DevByLabelDir) ?
                                new DirectoryInfo(DevByLabelDir).GetFiles() : Enumerable.Empty<FileInfo>();
 
             var labelDevPathPairs = labelDirEnum
-                                    .Select(x => (GetSymlinkTarget(x.FullName), x.Name));
+                                    .Select(x => (GetSymlinkTarget(x.FullName), UnescapeDeviceLabel(x.Name)));
 
             var q1 = from mount in fProcMounts
                      join device in fProcPartitions on mount.Item1 equals device.Item2
@@ -64,7 +71,7 @@ namespace Avalonia.FreeDesktop
                      {
                          VolumePath = mount.Item2,
                          VolumeSizeBytes = device.Item1,
-                         VolumeLabel = x.Name
+                         VolumeLabel = x.Item2
                      };
 
             var mountVolInfos = q1.ToArray();

+ 3 - 3
src/Avalonia.FreeDesktop/NativeMethods.cs

@@ -14,15 +14,15 @@ namespace Avalonia.FreeDesktop
 
         public static string ReadLink(string path)
         {
-            var symlinkMaxSize = Encoding.ASCII.GetMaxByteCount(path.Length);
+            var symlinkSize = Encoding.UTF8.GetByteCount(path);
             var bufferSize = 4097; // PATH_MAX is (usually?) 4096, but we need to know if the result was truncated
 
-            var symlink = ArrayPool<byte>.Shared.Rent(symlinkMaxSize + 1);
+            var symlink = ArrayPool<byte>.Shared.Rent(symlinkSize + 1);
             var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
 
             try
             {
-                var symlinkSize = Encoding.UTF8.GetBytes(path, 0, path.Length, symlink, 0);
+                Encoding.UTF8.GetBytes(path, 0, path.Length, symlink, 0);
                 symlink[symlinkSize] = 0;
 
                 var size = readlink(symlink, buffer, bufferSize);

+ 7 - 0
src/Avalonia.Native/WindowImpl.cs

@@ -107,6 +107,13 @@ namespace Avalonia.Native
         private bool _isExtended;
         public bool IsClientAreaExtendedToDecorations => _isExtended;
 
+        public override void Show(bool activate, bool isDialog)
+        {
+            base.Show(activate, isDialog);
+            
+            InvalidateExtendedMargins();
+        }
+
         protected override bool ChromeHitTest (RawPointerEventArgs e)
         {
             if(_isExtended)

+ 1 - 0
src/Avalonia.Native/avn.idl

@@ -2,6 +2,7 @@
 @clr-access internal
 @clr-map bool int
 @cpp-preamble @@
+#pragma once
 #include "com.h"
 #include "stddef.h"
 @@

+ 10 - 8
src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml

@@ -1,4 +1,4 @@
-<Style xmlns="https://github.com/avaloniaui" 
+<Style xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Selector="OverlayPopupHost">
   <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
@@ -9,13 +9,15 @@
   <Setter Property="Template">
     <ControlTemplate>
       <!--  Do not forget to update Templated_Control_With_Popup_In_Template_Should_Set_TemplatedParent test  -->
-      <VisualLayerManager IsPopup="True">
-        <ContentPresenter Name="PART_ContentPresenter"
-                          Background="{TemplateBinding Background}"
-                          ContentTemplate="{TemplateBinding ContentTemplate}"
-                          Content="{TemplateBinding Content}"
-                          Padding="{TemplateBinding Padding}"/>
-      </VisualLayerManager>
+      <LayoutTransformControl LayoutTransform="{TemplateBinding Transform}">
+        <VisualLayerManager IsPopup="True">
+          <ContentPresenter Name="PART_ContentPresenter"
+                            Background="{TemplateBinding Background}"
+                            ContentTemplate="{TemplateBinding ContentTemplate}"
+                            Content="{TemplateBinding Content}"
+                            Padding="{TemplateBinding Padding}"/>
+        </VisualLayerManager>
+      </LayoutTransformControl>
     </ControlTemplate>
   </Setter>
 </Style>

+ 12 - 10
src/Avalonia.Themes.Default/Controls/PopupRoot.xaml

@@ -10,16 +10,18 @@
   <Setter Property="FontStyle" Value="Normal" />
   <Setter Property="Template">
     <ControlTemplate>
-      <Panel>
-        <Border Name="PART_TransparencyFallback" IsHitTestVisible="False" />
-        <VisualLayerManager IsPopup="True">
-          <ContentPresenter Name="PART_ContentPresenter"
-                            Background="{TemplateBinding Background}"
-                            ContentTemplate="{TemplateBinding ContentTemplate}"
-                            Content="{TemplateBinding Content}"
-                            Padding="{TemplateBinding Padding}"/>
-        </VisualLayerManager>
-      </Panel>
+      <LayoutTransformControl LayoutTransform="{TemplateBinding Transform}">
+        <Panel>
+          <Border Name="PART_TransparencyFallback" IsHitTestVisible="False" />
+          <VisualLayerManager IsPopup="True">
+            <ContentPresenter Name="PART_ContentPresenter"
+                              Background="{TemplateBinding Background}"
+                              ContentTemplate="{TemplateBinding ContentTemplate}"
+                              Content="{TemplateBinding Content}"
+                              Padding="{TemplateBinding Padding}"/>
+          </VisualLayerManager>
+        </Panel>
+      </LayoutTransformControl>
     </ControlTemplate>
   </Setter>
 </Style>

+ 1 - 0
src/Avalonia.Themes.Default/Controls/TextBox.xaml

@@ -76,6 +76,7 @@
                                     SelectionEnd="{TemplateBinding SelectionEnd}"
                                     TextAlignment="{TemplateBinding TextAlignment}"
                                     TextWrapping="{TemplateBinding TextWrapping}"
+                                    LineHeight="{TemplateBinding LineHeight}"
                                     PasswordChar="{TemplateBinding PasswordChar}"
                                     RevealPassword="{TemplateBinding RevealPassword}"
                                     SelectionBrush="{TemplateBinding SelectionBrush}"

+ 2 - 1
src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml

@@ -119,7 +119,8 @@
                    MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
                    MaxHeight="{TemplateBinding MaxDropDownHeight}"
                    PlacementTarget="Background"
-                   IsLightDismissEnabled="True">
+                   IsLightDismissEnabled="True"
+                   InheritsTransform="True">
               <Border x:Name="PopupBorder"
                       Background="{DynamicResource ComboBoxDropDownBackground}"
                       BorderBrush="{DynamicResource ComboBoxDropDownBorderBrush}"

+ 9 - 7
src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml

@@ -6,13 +6,15 @@
   <Setter Property="FontStyle" Value="Normal" />
   <Setter Property="Template">
     <ControlTemplate>
-      <VisualLayerManager IsPopup="True">
-        <ContentPresenter Name="PART_ContentPresenter"
-                          Background="{TemplateBinding Background}"
-                          ContentTemplate="{TemplateBinding ContentTemplate}"
-                          Content="{TemplateBinding Content}"
-                          Padding="{TemplateBinding Padding}"/>
-      </VisualLayerManager>
+      <LayoutTransformControl LayoutTransform="{TemplateBinding Transform}">
+        <VisualLayerManager IsPopup="True">
+          <ContentPresenter Name="PART_ContentPresenter"
+                            Background="{TemplateBinding Background}"
+                            ContentTemplate="{TemplateBinding ContentTemplate}"
+                            Content="{TemplateBinding Content}"
+                            Padding="{TemplateBinding Padding}"/>
+        </VisualLayerManager>
+      </LayoutTransformControl>
     </ControlTemplate>
   </Setter>
 </Style>

+ 8 - 6
src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml

@@ -10,16 +10,18 @@
     <Setter Property="FontStyle" Value="Normal" />
     <Setter Property="Template">
       <ControlTemplate>
-        <Panel>
-          <Border Name="PART_TransparencyFallback" IsHitTestVisible="False" />
-          <VisualLayerManager IsPopup="True">
-            <ContentPresenter Name="PART_ContentPresenter"
+        <LayoutTransformControl LayoutTransform="{TemplateBinding Transform}">
+          <Panel>
+            <Border Name="PART_TransparencyFallback" IsHitTestVisible="False" />
+            <VisualLayerManager IsPopup="True">
+              <ContentPresenter Name="PART_ContentPresenter"
                               Background="{TemplateBinding Background}"
                               ContentTemplate="{TemplateBinding ContentTemplate}"
                               Content="{TemplateBinding Content}"
                               Padding="{TemplateBinding Padding}"/>
-          </VisualLayerManager>
-        </Panel>
+            </VisualLayerManager>
+          </Panel>
+        </LayoutTransformControl>
       </ControlTemplate>
     </Setter>
   </Style>

+ 1 - 0
src/Avalonia.Themes.Fluent/Controls/TextBox.xaml

@@ -89,6 +89,7 @@
                                       SelectionEnd="{TemplateBinding SelectionEnd}"
                                       TextAlignment="{TemplateBinding TextAlignment}"
                                       TextWrapping="{TemplateBinding TextWrapping}"
+                                      LineHeight="{TemplateBinding LineHeight}"
                                       PasswordChar="{TemplateBinding PasswordChar}"
                                       RevealPassword="{TemplateBinding RevealPassword}"
                                       SelectionBrush="{TemplateBinding SelectionBrush}"

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä