diff --git a/Avalonia.sln b/Avalonia.sln
index ea30514c3e..c8e513f94c 100644
--- a/Avalonia.sln
+++ b/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}"
diff --git a/build/Magick.NET-Q16-AnyCPU.props b/build/ImageSharp.props
similarity index 63%
rename from build/Magick.NET-Q16-AnyCPU.props
rename to build/ImageSharp.props
index 21d9cdcb1f..178c274ac9 100644
--- a/build/Magick.NET-Q16-AnyCPU.props
+++ b/build/ImageSharp.props
@@ -1,5 +1,5 @@
-
+
diff --git a/native/Avalonia.Native/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h
index 2b0338d099..a59915777f 100644
--- a/native/Avalonia.Native/inc/rendertarget.h
+++ b/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;
diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.h b/native/Avalonia.Native/src/OSX/AutoFitContentView.h
index 68c9fb8dc8..7f1f764141 100644
--- a/native/Avalonia.Native/src/OSX/AutoFitContentView.h
+++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.h
@@ -3,8 +3,10 @@
// Copyright (c) 2022 Avalonia. All rights reserved.
//
+#pragma once
+
#import
-#import "avalonia-native.h"
+#include "avalonia-native.h"
@interface AutoFitContentView : NSView
-(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content;
diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm
index 92d6f67a91..314c579b76 100644
--- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm
+++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm
@@ -4,7 +4,9 @@
//
#include "AvnView.h"
-#import "AutoFitContentView.h"
+#include "AutoFitContentView.h"
+#include "WindowInterfaces.h"
+#include "WindowProtocol.h"
@implementation AutoFitContentView
{
@@ -83,7 +85,7 @@
_settingSize = true;
[super setFrameSize:newSize];
- auto window = objc_cast([self window]);
+ auto window = static_cast>([self window]);
// TODO get actual titlebar size
diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
index 88e4c0c682..6fc3977d4e 100644
--- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
+++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
@@ -9,16 +9,22 @@
/* 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 */; };
@@ -40,24 +46,29 @@
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 = ""; };
+ 1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = ""; };
1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; };
183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; };
+ 1839155B28B20FFB672D29C6 /* AvnWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnWindow.mm; sourceTree = ""; };
183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; };
18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFitContentView.h; sourceTree = ""; };
1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; };
18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; };
1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; };
+ 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = ""; };
183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; };
+ 18391BB698579F40F1783F31 /* PopupImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PopupImpl.mm; sourceTree = ""; };
18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; };
18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; };
18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; };
+ 18391DB45C7D892E61BF388C /* WindowInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowInterfaces.h; sourceTree = ""; };
18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; };
1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; };
1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; };
@@ -72,7 +83,6 @@
37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; };
37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; };
37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = ""; };
- 37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = ""; };
37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = ""; };
37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = ""; };
37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; };
@@ -86,7 +96,6 @@
AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; };
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 = ""; };
AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; };
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 = ""; };
@@ -142,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 */,
@@ -166,6 +173,12 @@
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 = "";
};
@@ -193,6 +206,9 @@
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;
};
@@ -271,12 +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;
};
diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm
new file mode 100644
index 0000000000..2365189010
--- /dev/null
+++ b/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"
+
diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h
index c6dd90150f..86a68d34c5 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.h
+++ b/native/Avalonia.Native/src/OSX/AvnView.h
@@ -2,17 +2,15 @@
// Created by Dan Walmsley on 05/05/2022.
// Copyright (c) 2022 Avalonia. All rights reserved.
//
-
+#pragma once
#import
#import
#import
-#include "window.h"
-#import "comimpl.h"
-#import "common.h"
-#import "WindowImpl.h"
-#import "KeyTransform.h"
+#include "common.h"
+#include "WindowImpl.h"
+#include "KeyTransform.h"
@class AvnAccessibilityElement;
diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm
index 24cbc25502..02526afbcb 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.mm
+++ b/native/Avalonia.Native/src/OSX/AvnView.mm
@@ -4,8 +4,9 @@
//
#import
-#import "AvnView.h"
+#include "AvnView.h"
#include "automation.h"
+#import "WindowInterfaces.h"
@implementation AvnView
{
@@ -194,7 +195,12 @@
- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled
{
- auto parentWindow = objc_cast([self window]);
+ if(_parent == nullptr)
+ {
+ return TRUE;
+ }
+
+ auto parentWindow = _parent->GetWindowProtocol();
if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
{
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm
similarity index 75%
rename from native/Avalonia.Native/src/OSX/window.mm
rename to native/Avalonia.Native/src/OSX/AvnWindow.mm
index 68a459d088..6ff19ead68 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm
@@ -1,16 +1,34 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+
+#import
+#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
#include "common.h"
-#import "window.h"
#include "menu.h"
#include "automation.h"
-#import "WindowBaseImpl.h"
+#include "WindowBaseImpl.h"
#include "WindowImpl.h"
#include "AvnView.h"
+#include "WindowInterfaces.h"
+#include "PopupImpl.h"
-@implementation AvnWindow
+@implementation CLASS_NAME
{
ComPtr _parent;
- bool _canBecomeKeyAndMain;
bool _closed;
bool _isEnabled;
bool _isExtended;
@@ -66,7 +84,7 @@
- (void)pollModalSession:(nonnull NSModalSession)session
{
auto response = [NSApp runModalSession:session];
-
+
if(response == NSModalResponseContinue)
{
dispatch_async(dispatch_get_main_queue(), ^{
@@ -85,18 +103,18 @@
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
@@ -108,22 +126,22 @@
-(void) showAppMenuOnly
{
auto appMenuItem = ::GetAppMenuItem();
-
+
if(appMenuItem != nullptr)
{
auto appMenu = ::GetAppMenu();
-
+
auto nativeAppMenu = dynamic_cast(appMenu);
-
+
[[appMenuItem menu] removeItem:appMenuItem];
-
+
if(_menu != nullptr)
{
[_menu setHasGlobalMenuItem:false];
}
-
+
[nativeAppMenu->GetNative() addItem:appMenuItem];
-
+
[NSApp setMenu:nativeAppMenu->GetNative()];
}
}
@@ -134,27 +152,27 @@
{
menu = [AvnMenu new];
}
-
+
_menu = menu;
}
--(void) setCanBecomeKeyAndMain
+-(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
{
- _canBecomeKeyAndMain = true;
-}
+ // 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];
--(AvnWindow*) initWithParent: (WindowBaseImpl*) parent
-{
- self = [super init];
[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;
}
@@ -162,12 +180,12 @@
- (BOOL)windowShouldClose:(NSWindow *)sender
{
auto window = dynamic_cast(_parent.getRaw());
-
+
if(window != nullptr)
{
return !window->WindowEvents->Closing();
}
-
+
return true;
}
@@ -191,27 +209,26 @@
-(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])
{
- // 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(uch);
- if(ch == nil)
- continue;
- if (ch.isDialog)
- return false;
- }
-
- return true;
+ auto ch = static_cast>(uch);
+ if(ch == nil)
+ continue;
+ if (ch.isDialog)
+ return false;
}
-
- return false;
+
+ return true;
}
-(BOOL)canBecomeMainWindow
{
- return _canBecomeKeyAndMain;
+#ifdef IS_NSPANEL
+ return false;
+#else
+ return true;
+#endif
}
-(bool)shouldTryToHandleEvents
@@ -227,7 +244,7 @@
-(void)becomeKeyWindow
{
[self showWindowMenuWithAppMenu];
-
+
if(_parent != nullptr)
{
_parent->BaseEvents->Activated();
@@ -238,7 +255,8 @@
-(void) restoreParentWindow;
{
- auto parent = objc_cast([self parentWindow]);
+ auto parent = [self parentWindow];
+
if(parent != nil)
{
[parent removeChildWindow:self];
@@ -248,7 +266,7 @@
- (void)windowDidMiniaturize:(NSNotification *)notification
{
auto parent = dynamic_cast(_parent.operator->());
-
+
if(parent != nullptr)
{
parent->WindowStateChanged();
@@ -258,7 +276,7 @@
- (void)windowDidDeminiaturize:(NSNotification *)notification
{
auto parent = dynamic_cast(_parent.operator->());
-
+
if(parent != nullptr)
{
parent->WindowStateChanged();
@@ -268,7 +286,7 @@
- (void)windowDidResize:(NSNotification *)notification
{
auto parent = dynamic_cast(_parent.operator->());
-
+
if(parent != nullptr)
{
parent->WindowStateChanged();
@@ -278,7 +296,7 @@
- (void)windowWillExitFullScreen:(NSNotification *)notification
{
auto parent = dynamic_cast(_parent.operator->());
-
+
if(parent != nullptr)
{
parent->StartStateTransition();
@@ -288,22 +306,22 @@
- (void)windowDidExitFullScreen:(NSNotification *)notification
{
auto parent = dynamic_cast(_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();
}
}
@@ -311,7 +329,7 @@
- (void)windowWillEnterFullScreen:(NSNotification *)notification
{
auto parent = dynamic_cast(_parent.operator->());
-
+
if(parent != nullptr)
{
parent->StartStateTransition();
@@ -321,7 +339,7 @@
- (void)windowDidEnterFullScreen:(NSNotification *)notification
{
auto parent = dynamic_cast(_parent.operator->());
-
+
if(parent != nullptr)
{
parent->EndStateTransition();
@@ -338,20 +356,20 @@
{
if(_parent)
_parent->BaseEvents->Deactivated();
-
+
[self showAppMenuOnly];
-
+
[super resignKeyWindow];
}
- (void)windowDidMove:(NSNotification *)notification
{
AvnPoint position;
-
+
if(_parent != nullptr)
{
auto cparent = dynamic_cast(_parent.getRaw());
-
+
if(cparent != nullptr)
{
if(cparent->WindowState() == Maximized)
@@ -359,7 +377,7 @@
cparent->SetWindowState(Normal);
}
}
-
+
_parent->GetPosition(&position);
_parent->BaseEvents->PositionChanged(position);
}
@@ -374,7 +392,7 @@
- (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(_parent.getRaw()) != nullptr)
{
@@ -385,95 +403,39 @@
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([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
}
}
- break;
-
+ break;
+
case NSEventTypeMouseEntered:
{
_parent->UpdateCursor();
}
- break;
-
+ break;
+
case NSEventTypeMouseExited:
{
[[NSCursor arrowCursor] set];
}
- break;
-
+ 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 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(new PopupImpl(events, gl));
- return ptr;
- }
+- (void)disconnectParent {
+ _parent = nullptr;
}
-extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
-{
- @autoreleasepool
- {
- IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl);
- return ptr;
- }
-}
+@end
+
diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h
index adc0dd1990..ae64a53e7d 100644
--- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h
+++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h
@@ -10,8 +10,8 @@
struct INSWindowHolder
{
- virtual AvnWindow* _Nonnull GetNSWindow () = 0;
- virtual AvnView* _Nonnull GetNSView () = 0;
+ virtual NSWindow* _Nonnull GetNSWindow () = 0;
+ virtual NSView* _Nonnull GetNSView () = 0;
};
#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H
diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.h b/native/Avalonia.Native/src/OSX/PopupImpl.h
new file mode 100644
index 0000000000..451019a6a4
--- /dev/null
+++ b/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
diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm
new file mode 100644
index 0000000000..64a8780158
--- /dev/null
+++ b/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
+#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 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(new PopupImpl(events, gl));
+ return ptr;
+ }
+}
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h
index 7509f93c01..9a43c158fe 100644
--- a/native/Avalonia.Native/src/OSX/ResizeScope.h
+++ b/native/Avalonia.Native/src/OSX/ResizeScope.h
@@ -6,7 +6,6 @@
#ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H
#define AVALONIA_NATIVE_OSX_RESIZESCOPE_H
-#include "window.h"
#include "avalonia-native.h"
@class AvnView;
diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm
index 90e7f5cf15..9f1177af8b 100644
--- a/native/Avalonia.Native/src/OSX/ResizeScope.mm
+++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm
@@ -5,7 +5,7 @@
#import
#include "ResizeScope.h"
-#import "AvnView.h"
+#include "AvnView.h"
ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) {
_view = view;
diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm
index 21ad9cfa7c..535b6c3b66 100644
--- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm
+++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm
@@ -1,5 +1,4 @@
#include "common.h"
-#include "window.h"
#include "INSWindowHolder.h"
class SystemDialogs : public ComSingleObject
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
index d2690cee41..eff13bcb23 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
@@ -6,10 +6,12 @@
#ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
#define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
-#import "rendertarget.h"
+#include "rendertarget.h"
#include "INSWindowHolder.h"
@class AutoFitContentView;
+@class AvnMenu;
+@protocol AvnWindowProtocol;
class WindowBaseImpl : public virtual ComObject,
public virtual IAvnWindowBase,
@@ -24,18 +26,19 @@ BEGIN_INTERFACE_MAP()
INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase)
END_INTERFACE_MAP()
- virtual ~WindowBaseImpl() {
- View = NULL;
- Window = NULL;
- }
+ virtual ~WindowBaseImpl();
AutoFitContentView *StandardContainer;
AvnView *View;
- AvnWindow *Window;
+ NSWindow * Window;
ComPtr BaseEvents;
ComPtr _glContext;
NSObject *renderTarget;
AvnPoint lastPositionSet;
+ NSSize lastSize;
+ NSSize lastMinSize;
+ NSSize lastMaxSize;
+ AvnMenu* lastMenu;
NSString *_lastTitle;
bool _shown;
@@ -51,9 +54,9 @@ BEGIN_INTERFACE_MAP()
virtual HRESULT ObtainNSViewHandleRetained(void **ret) override;
- virtual AvnWindow *GetNSWindow() override;
+ virtual NSWindow *GetNSWindow() override;
- virtual AvnView *GetNSView() override;
+ virtual NSView *GetNSView() override;
virtual HRESULT Show(bool activate, bool isDialog) override;
@@ -111,11 +114,17 @@ BEGIN_INTERFACE_MAP()
virtual bool IsDialog();
+ id GetWindowProtocol ();
+
protected:
virtual NSWindowStyleMask GetStyle();
void UpdateStyle();
+private:
+ void CreateNSWindow (bool isDialog);
+ void CleanNSWindow ();
+ void InitialiseNSWindow ();
};
#endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
index fb67addce1..db5eb54e3f 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
@@ -5,13 +5,21 @@
#import
#include "common.h"
-#import "window.h"
-#import "AvnView.h"
+#include "AvnView.h"
#include "menu.h"
#include "automation.h"
-#import "cursor.h"
+#include "cursor.h"
#include "ResizeScope.h"
-#import "AutoFitContentView.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;
@@ -22,17 +30,15 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *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;
+ lastSize = NSSize { 100, 100 };
+ lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX};
+ lastMinSize = NSSize { 0, 0 };
_lastTitle = @"";
- [Window setStyleMask:NSWindowStyleMaskBorderless];
- [Window setBackingType:NSBackingStoreBuffered];
-
- [Window setOpaque:false];
+ Window = nullptr;
+ lastMenu = nullptr;
}
HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
@@ -59,11 +65,11 @@ HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) {
return S_OK;
}
-AvnWindow *WindowBaseImpl::GetNSWindow() {
+NSWindow *WindowBaseImpl::GetNSWindow() {
return Window;
}
-AvnView *WindowBaseImpl::GetNSView() {
+NSView *WindowBaseImpl::GetNSView() {
return View;
}
@@ -83,6 +89,9 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
START_COM_CALL;
@autoreleasepool {
+ CreateNSWindow(isDialog);
+ InitialiseNSWindow();
+
SetPosition(lastPositionSet);
UpdateStyle();
@@ -125,7 +134,8 @@ HRESULT WindowBaseImpl::Hide() {
@autoreleasepool {
if (Window != nullptr) {
[Window orderOut:Window];
- [Window restoreParentWindow];
+
+ [GetWindowProtocol() restoreParentWindow];
}
return S_OK;
@@ -225,8 +235,13 @@ HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) {
START_COM_CALL;
@autoreleasepool {
- [Window setMinSize:ToNSSize(minSize)];
- [Window setMaxSize:ToNSSize(maxSize)];
+ lastMinSize = ToNSSize(minSize);
+ lastMaxSize = ToNSSize(maxSize);
+
+ if(Window != nullptr) {
+ [Window setContentMinSize:lastMinSize];
+ [Window setContentMaxSize:lastMaxSize];
+ }
return S_OK;
}
@@ -243,8 +258,8 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
auto resizeBlock = ResizeScope(View, reason);
@autoreleasepool {
- auto maxSize = [Window maxSize];
- auto minSize = [Window minSize];
+ auto maxSize = lastMaxSize;
+ auto minSize = lastMinSize;
if (x < minSize.width) {
x = minSize.width;
@@ -267,8 +282,11 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
BaseEvents->Resized(AvnSize{x, y}, reason);
}
- [Window setContentSize:NSSize{x, y}];
- [Window invalidateShadow];
+ lastSize = NSSize {x, y};
+
+ if(Window != nullptr) {
+ [Window setContentSize:lastSize];
+ }
}
@finally {
_inResize = false;
@@ -293,12 +311,14 @@ HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) {
auto nativeMenu = dynamic_cast(menu);
- auto nsmenu = nativeMenu->GetNative();
+ lastMenu = nativeMenu->GetNative();
- [Window applyMenu:nsmenu];
+ if(Window != nullptr) {
+ [GetWindowProtocol() applyMenu:lastMenu];
- if ([Window isKeyWindow]) {
- [Window showWindowMenuWithAppMenu];
+ if ([Window isKeyWindow]) {
+ [GetWindowProtocol() showWindowMenuWithAppMenu];
+ }
}
return S_OK;
@@ -502,4 +522,68 @@ NSWindowStyleMask WindowBaseImpl::GetStyle() {
void WindowBaseImpl::UpdateStyle() {
[Window setStyleMask:GetStyle()];
-}
\ No newline at end of file
+}
+
+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 WindowBaseImpl::GetWindowProtocol() {
+ if(Window == nullptr)
+ {
+ return nullptr;
+ }
+
+ return static_cast>(Window);
+}
+
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
+{
+ @autoreleasepool
+ {
+ IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl);
+ return ptr;
+ }
+}
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h
index b229921baa..a4ee4f447c 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.h
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.h
@@ -6,7 +6,6 @@
#ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H
#define AVALONIA_NATIVE_OSX_WINDOWIMPL_H
-
#import "WindowBaseImpl.h"
#include "IWindowStateChanged.h"
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm
index 0d325c4490..7ab2b2b5fc 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm
@@ -4,10 +4,10 @@
//
#import
-#import "window.h"
-#import "AutoFitContentView.h"
-#import "AvnView.h"
+#include "AutoFitContentView.h"
+#include "AvnView.h"
#include "automation.h"
+#include "WindowProtocol.h"
WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) {
_isClientAreaExtended = false;
@@ -20,7 +20,6 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase
_lastWindowState = Normal;
_actualWindowState = Normal;
WindowEvents = events;
- [Window setCanBecomeKeyAndMain];
[Window disableCursorRects];
[Window setTabbingMode:NSWindowTabbingModeDisallowed];
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
@@ -56,8 +55,20 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) {
@autoreleasepool {
_isDialog = isDialog;
+
+ bool created = Window == nullptr;
+
WindowBaseImpl::Show(activate, isDialog);
+ if(created)
+ {
+ if(_isClientAreaExtended)
+ {
+ [GetWindowProtocol() setIsExtended:true];
+ SetExtendClientArea(true);
+ }
+ }
+
HideOrShowTrafficLights();
return SetWindowState(_lastWindowState);
@@ -68,7 +79,7 @@ HRESULT WindowImpl::SetEnabled(bool enable) {
START_COM_CALL;
@autoreleasepool {
- [Window setEnabled:enable];
+ [GetWindowProtocol() setEnabled:enable];
return S_OK;
}
}
@@ -328,37 +339,39 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) {
@autoreleasepool {
_isClientAreaExtended = enable;
- if (enable) {
- Window.titleVisibility = NSWindowTitleHidden;
+ if(Window != nullptr) {
+ if (enable) {
+ Window.titleVisibility = NSWindowTitleHidden;
- [Window setTitlebarAppearsTransparent:true];
+ [Window setTitlebarAppearsTransparent:true];
- auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
+ auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
- if (wantsTitleBar) {
- [StandardContainer ShowTitleBar:true];
- } else {
- [StandardContainer ShowTitleBar:false];
- }
+ if (wantsTitleBar) {
+ [StandardContainer ShowTitleBar:true];
+ } else {
+ [StandardContainer ShowTitleBar:false];
+ }
- if (_extendClientHints & AvnOSXThickTitleBar) {
- Window.toolbar = [NSToolbar new];
- Window.toolbar.showsBaselineSeparator = 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;
}
- } else {
- Window.titleVisibility = NSWindowTitleVisible;
- Window.toolbar = nullptr;
- [Window setTitlebarAppearsTransparent:false];
- View.layer.zPosition = 0;
- }
- [Window setIsExtended:enable];
+ [GetWindowProtocol() setIsExtended:enable];
- HideOrShowTrafficLights();
+ HideOrShowTrafficLights();
- UpdateStyle();
+ UpdateStyle();
+ }
return S_OK;
}
@@ -383,7 +396,7 @@ HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) {
return E_POINTER;
}
- *ret = [Window getExtendedTitleBarHeight];
+ *ret = [GetWindowProtocol() getExtendedTitleBarHeight];
return S_OK;
}
@@ -508,7 +521,7 @@ bool WindowImpl::IsDialog() {
}
NSWindowStyleMask WindowImpl::GetStyle() {
- unsigned long s = NSWindowStyleMaskBorderless;
+ unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless;
switch (_decorations) {
case SystemDecorationsNone:
diff --git a/native/Avalonia.Native/src/OSX/WindowInterfaces.h b/native/Avalonia.Native/src/OSX/WindowInterfaces.h
new file mode 100644
index 0000000000..6e6d62e85e
--- /dev/null
+++ b/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
+#import
+#include "WindowProtocol.h"
+#include "WindowBaseImpl.h"
+
+@interface AvnWindow : NSWindow
+-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+@end
+
+@interface AvnPanel : NSPanel
+-(AvnPanel* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+@end
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h
similarity index 58%
rename from native/Avalonia.Native/src/OSX/window.h
rename to native/Avalonia.Native/src/OSX/WindowProtocol.h
index e9b98ce9ae..92194706de 100644
--- a/native/Avalonia.Native/src/OSX/window.h
+++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h
@@ -1,15 +1,15 @@
-#ifndef window_h
-#define window_h
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
-#import "avalonia-native.h"
+#pragma once
-@class AvnMenu;
+#import
-class WindowBaseImpl;
+@class AvnMenu;
-@interface AvnWindow : NSWindow
--(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
--(void) setCanBecomeKeyAndMain;
+@protocol AvnWindowProtocol
-(void) pollModalSession: (NSModalSession _Nonnull) session;
-(void) restoreParentWindow;
-(bool) shouldTryToHandleEvents;
@@ -20,7 +20,6 @@ class WindowBaseImpl;
-(double) getExtendedTitleBarHeight;
-(void) setIsExtended:(bool)value;
+-(void) disconnectParent;
-(bool) isDialog;
-@end
-
-#endif /* window_h */
+@end
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h
index 1727d21f27..367df3619d 100644
--- a/native/Avalonia.Native/src/OSX/automation.h
+++ b/native/Avalonia.Native/src/OSX/automation.h
@@ -1,5 +1,6 @@
-#import
+#pragma once
+#import
NS_ASSUME_NONNULL_BEGIN
class IAvnAutomationPeer;
diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm
index 7a0b3b8127..d0c8d7a9db 100644
--- a/native/Avalonia.Native/src/OSX/automation.mm
+++ b/native/Avalonia.Native/src/OSX/automation.mm
@@ -1,9 +1,8 @@
#include "common.h"
-#import "automation.h"
-#import "window.h"
+#include "automation.h"
#include "AvnString.h"
-#import "INSWindowHolder.h"
-#import "AvnView.h"
+#include "INSWindowHolder.h"
+#include "AvnView.h"
@interface AvnAccessibilityElement (Events)
- (void) raiseChildrenChanged;
diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm
index 011f881e94..6ee86b21ae 100644
--- a/native/Avalonia.Native/src/OSX/main.mm
+++ b/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";
diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm
index fd74edd772..b05588a441 100644
--- a/native/Avalonia.Native/src/OSX/menu.mm
+++ b/native/Avalonia.Native/src/OSX/menu.mm
@@ -1,7 +1,6 @@
#include "common.h"
#include "menu.h"
-#import "window.h"
#include "KeyTransform.h"
#include
#include /* For kVK_ constants, and TIS functions. */
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 4b81935452..4464413e63 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/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()
diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
index 2b0c30f311..c44024d952 100644
--- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
+++ b/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;
diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs
index 9972e72dd4..a05c9c5da3 100644
--- a/src/Avalonia.Base/Collections/AvaloniaList.cs
+++ b/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();
+ }
}
}
}
diff --git a/src/Avalonia.Base/Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs
index 418cd55e41..47c994a350 100644
--- a/src/Avalonia.Base/Layout/UniformGridLayout.cs
+++ b/src/Avalonia.Base/Layout/UniformGridLayout.cs
@@ -116,7 +116,7 @@ namespace Avalonia.Layout
/// Defines the property.
///
public static readonly StyledProperty MaximumRowsOrColumnsProperty =
- AvaloniaProperty.Register(nameof(MinItemWidth));
+ AvaloniaProperty.Register(nameof(MaximumRowsOrColumns));
///
/// Defines the property.
diff --git a/src/Avalonia.Base/Media/ConicGradientBrush.cs b/src/Avalonia.Base/Media/ConicGradientBrush.cs
index 7c1266fa17..4b50019ddc 100644
--- a/src/Avalonia.Base/Media/ConicGradientBrush.cs
+++ b/src/Avalonia.Base/Media/ConicGradientBrush.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Media
/// Defines the property.
///
public static readonly StyledProperty CenterProperty =
- AvaloniaProperty.Register(
+ AvaloniaProperty.Register(
nameof(Center),
RelativePoint.Center);
@@ -19,7 +19,7 @@ namespace Avalonia.Media
/// Defines the property.
///
public static readonly StyledProperty AngleProperty =
- AvaloniaProperty.Register(
+ AvaloniaProperty.Register(
nameof(Angle),
0);
diff --git a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs
index c6845485dc..827a02334a 100644
--- a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs
+++ b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs
@@ -5,12 +5,11 @@ namespace Avalonia.Threading
public class ThreadSafeObjectPool where T : class, new()
{
private Stack _stack = new Stack();
- private object _lock = new object();
public static ThreadSafeObjectPool Default { get; } = new ThreadSafeObjectPool();
public T Get()
{
- lock (_lock)
+ lock (_stack)
{
if(_stack.Count == 0)
return new T();
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index ee2378101a..2b122d4174 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -63,7 +63,7 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly StyledProperty PlacementRectProperty =
- AvaloniaProperty.Register(nameof(PlacementRect));
+ Popup.PlacementRectProperty.AddOwner();
///
/// Defines the property.
diff --git a/src/Avalonia.Controls/DockPanel.cs b/src/Avalonia.Controls/DockPanel.cs
index 8e23555c2d..3e3ed509b5 100644
--- a/src/Avalonia.Controls/DockPanel.cs
+++ b/src/Avalonia.Controls/DockPanel.cs
@@ -34,7 +34,7 @@ namespace Avalonia.Controls
///
public static readonly StyledProperty LastChildFillProperty =
AvaloniaProperty.Register(
- nameof(LastChildFillProperty),
+ nameof(LastChildFill),
defaultValue: true);
///
diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs
index 933788f9ea..080326606e 100644
--- a/src/Avalonia.Controls/MaskedTextBox.cs
+++ b/src/Avalonia.Controls/MaskedTextBox.cs
@@ -32,7 +32,7 @@ namespace Avalonia.Controls
AvaloniaProperty.Register(nameof(Mask), string.Empty);
public static new readonly StyledProperty PasswordCharProperty =
- AvaloniaProperty.Register(nameof(PasswordChar), '\0');
+ AvaloniaProperty.Register(nameof(PasswordChar), '\0');
public static readonly StyledProperty PromptCharProperty =
AvaloniaProperty.Register(nameof(PromptChar), '_');
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 0785149a73..07061940b5 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -26,13 +26,13 @@ namespace Avalonia.Controls.Presenters
AvaloniaProperty.Register(nameof(PasswordChar));
public static readonly StyledProperty SelectionBrushProperty =
- AvaloniaProperty.Register(nameof(SelectionBrushProperty));
+ AvaloniaProperty.Register(nameof(SelectionBrush));
public static readonly StyledProperty SelectionForegroundBrushProperty =
- AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty));
+ AvaloniaProperty.Register(nameof(SelectionForegroundBrush));
public static readonly StyledProperty CaretBrushProperty =
- AvaloniaProperty.Register(nameof(CaretBrushProperty));
+ AvaloniaProperty.Register(nameof(CaretBrush));
public static readonly DirectProperty SelectionStartProperty =
TextBox.SelectionStartProperty.AddOwner(
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index 7a7e41b029..95e5e25c42 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -27,7 +27,7 @@ namespace Avalonia.Controls.Primitives
#pragma warning restore CS0612 // Type or member is obsolete
{
public static readonly StyledProperty WindowManagerAddShadowHintProperty =
- AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false);
+ AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false);
///
/// Defines the property.
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index bff6799792..164aab32db 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -94,7 +94,7 @@ namespace Avalonia.Controls.Primitives
/// Defines the property.
///
public static readonly StyledProperty IsTextSearchEnabledProperty =
- AvaloniaProperty.Register(nameof(IsTextSearchEnabled), false);
+ AvaloniaProperty.Register(nameof(IsTextSearchEnabled), false);
///
/// Event that should be raised by items that implement to
diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs
index 49f0cda982..14ec7a2849 100644
--- a/src/Avalonia.Controls/Primitives/Track.cs
+++ b/src/Avalonia.Controls/Primitives/Track.cs
@@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives
AvaloniaProperty.Register
public static readonly StyledProperty TickPlacementProperty =
- AvaloniaProperty.Register(nameof(TickPlacement), 0d);
+ AvaloniaProperty.Register(nameof(TickPlacement), 0d);
///
/// Defines the property.
@@ -197,7 +197,7 @@ namespace Avalonia.Controls
if (_track != null)
{
- _track.IsThumbDragHandled = true;
+ _track.IgnoreThumbDrag = true;
}
if (_decreaseButton != null)
diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs
index ae1605a985..532cb1d329 100644
--- a/src/Avalonia.Controls/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView.cs
@@ -138,7 +138,7 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly StyledProperty PaneTemplateProperty =
- AvaloniaProperty.Register(nameof(PaneTemplate));
+ AvaloniaProperty.Register(nameof(PaneTemplate));
///
/// Defines the property
diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index 0be58e7fcc..45e66828c1 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/src/Avalonia.Controls/TextBox.cs
@@ -55,13 +55,13 @@ namespace Avalonia.Controls
AvaloniaProperty.Register(nameof(PasswordChar));
public static readonly StyledProperty SelectionBrushProperty =
- AvaloniaProperty.Register(nameof(SelectionBrushProperty));
+ AvaloniaProperty.Register(nameof(SelectionBrush));
public static readonly StyledProperty SelectionForegroundBrushProperty =
- AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty));
+ AvaloniaProperty.Register(nameof(SelectionForegroundBrush));
public static readonly StyledProperty CaretBrushProperty =
- AvaloniaProperty.Register(nameof(CaretBrushProperty));
+ AvaloniaProperty.Register(nameof(CaretBrush));
public static readonly DirectProperty SelectionStartProperty =
AvaloniaProperty.RegisterDirect(
diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs
index c082bdb1b8..b5af927ea0 100644
--- a/src/Avalonia.Native/WindowImpl.cs
+++ b/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)
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index 8092004989..d6ef0f8918 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/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"
@@
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs
index 871a2a2045..e76e2cd46e 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs
@@ -55,7 +55,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return cached.get;
}
- var name = lst.Count == 0 ? key : key + "_" + Guid.NewGuid().ToString("N");
+ var name = lst.Count == 0 ? key : key + "_" + context.Configuration.IdentifierGenerator.GenerateIdentifierPart();
var field = _builder.DefineField(types.IPropertyInfo, name + "!Field", false, true);
diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs
index f5ae4cc1e0..b59f272b0c 100644
--- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs
@@ -146,6 +146,23 @@ namespace Avalonia.Base.UnitTests.Collections
Assert.True(raised);
}
+ [Fact]
+ public void AddRange_IEnumerable_Should_Raise_Count_PropertyChanged()
+ {
+ var target = new AvaloniaList(new[] { 1, 2, 3, 4, 5 });
+ var raised = false;
+
+ target.PropertyChanged += (s, e) => {
+ Assert.Equal(e.PropertyName, nameof(target.Count));
+ Assert.Equal(target.Count, 7);
+ raised = true;
+ };
+
+ target.AddRange(Enumerable.Range(6, 2));
+
+ Assert.True(raised);
+ }
+
[Fact]
public void AddRange_Items_Should_Raise_Correct_CollectionChanged()
{
diff --git a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
index e7f1552370..52acc78db1 100644
--- a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
+++ b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
@@ -19,6 +19,6 @@
-
+
diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs
index b70c721085..523876500f 100644
--- a/tests/Avalonia.RenderTests/TestBase.cs
+++ b/tests/Avalonia.RenderTests/TestBase.cs
@@ -1,10 +1,9 @@
using System.IO;
using System.Runtime.CompilerServices;
-using ImageMagick;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Rendering;
-
+using SixLabors.ImageSharp;
using Xunit;
using Avalonia.Platform;
using System.Threading.Tasks;
@@ -12,6 +11,8 @@ using System;
using System.Threading;
using Avalonia.Media;
using Avalonia.Threading;
+using SixLabors.ImageSharp.PixelFormats;
+using Image = SixLabors.ImageSharp.Image;
#if AVALONIA_SKIA
using Avalonia.Skia;
#else
@@ -119,12 +120,12 @@ namespace Avalonia.Direct2D1.RenderTests
var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png");
var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png");
- using (var expected = new MagickImage(expectedPath))
- using (var immediate = new MagickImage(immediatePath))
- using (var deferred = new MagickImage(deferredPath))
+ using (var expected = Image.Load(expectedPath))
+ using (var immediate = Image.Load(immediatePath))
+ using (var deferred = Image.Load(deferredPath))
{
- double immediateError = expected.Compare(immediate, ErrorMetric.RootMeanSquared);
- double deferredError = expected.Compare(deferred, ErrorMetric.RootMeanSquared);
+ var immediateError = CompareImages(immediate, expected);
+ var deferredError = CompareImages(deferred, expected);
if (immediateError > 0.022)
{
@@ -143,10 +144,10 @@ namespace Avalonia.Direct2D1.RenderTests
var expectedPath = Path.Combine(OutputPath, testName + ".expected.png");
var actualPath = Path.Combine(OutputPath, testName + ".out.png");
- using (var expected = new MagickImage(expectedPath))
- using (var actual = new MagickImage(actualPath))
+ using (var expected = Image.Load(expectedPath))
+ using (var actual = Image.Load(actualPath))
{
- double immediateError = expected.Compare(actual, ErrorMetric.RootMeanSquared);
+ double immediateError = CompareImages(actual, expected);
if (immediateError > 0.022)
{
@@ -154,6 +155,53 @@ namespace Avalonia.Direct2D1.RenderTests
}
}
}
+
+ ///
+ /// Calculates root mean square error for given two images.
+ /// Based roughly on ImageMagick implementation to ensure consistency.
+ ///
+ private static double CompareImages(Image actual, Image expected)
+ {
+ if (actual.Width != expected.Width || actual.Height != expected.Height)
+ {
+ throw new ArgumentException("Images have different resolutions");
+ }
+
+ var quantity = actual.Width * actual.Height;
+ double squaresError = 0;
+
+ const double scale = 1 / 255d;
+
+ for (var x = 0; x < actual.Width; x++)
+ {
+ double localError = 0;
+
+ for (var y = 0; y < actual.Height; y++)
+ {
+ var expectedAlpha = expected[x, y].A * scale;
+ var actualAlpha = actual[x, y].A * scale;
+
+ var r = scale * (expectedAlpha * expected[x, y].R - actualAlpha * actual[x, y].R);
+ var g = scale * (expectedAlpha * expected[x, y].G - actualAlpha * actual[x, y].G);
+ var b = scale * (expectedAlpha * expected[x, y].B - actualAlpha * actual[x, y].B);
+ var a = expectedAlpha - actualAlpha;
+
+ var error = r * r + g * g + b * b + a * a;
+
+ localError += error;
+ }
+
+ squaresError += localError;
+ }
+
+ var meanSquaresError = squaresError / quantity;
+
+ const int channelCount = 4;
+
+ meanSquaresError = meanSquaresError / channelCount;
+
+ return Math.Sqrt(meanSquaresError);
+ }
private string GetTestsDirectory()
{
diff --git a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj
index ffbcdd9e7c..d3f2b44968 100644
--- a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj
+++ b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj
@@ -20,7 +20,7 @@
-
+