diff --git a/Avalonia.sln b/Avalonia.sln index 4ac1538b902..b4117d8aac3 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -116,16 +116,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\SourceGenerators.props = build\SourceGenerators.props build\SourceLink.props = build\SourceLink.props build\System.Memory.props = build\System.Memory.props + build\TargetFrameworks.props = build\TargetFrameworks.props build\TrimmingEnable.props = build\TrimmingEnable.props build\UnitTests.NetFX.props = build\UnitTests.NetFX.props - build\XUnit.props = build\XUnit.props - build\TargetFrameworks.props = build\TargetFrameworks.props build\WarnAsErrors.props = build\WarnAsErrors.props + build\XUnit.props = build\XUnit.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}" ProjectSection(SolutionItems) = preProject build\BuildTargets.targets = build\BuildTargets.targets + build\DevSingleProject.targets = build\DevSingleProject.targets build\LegacyProject.targets = build\LegacyProject.targets build\UnitTests.NetCore.targets = build\UnitTests.NetCore.targets EndProjectSection @@ -237,8 +238,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - azure-pipelines.yml = azure-pipelines.yml azure-pipelines-integrationtests.yml = azure-pipelines-integrationtests.yml + azure-pipelines.yml = azure-pipelines.yml CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md CONTRIBUTING.md = CONTRIBUTING.md Directory.Build.props = Directory.Build.props @@ -294,24 +295,23 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Tizen", "sam EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks.UnitTest", "tests\Avalonia.Build.Tasks.UnitTest\Avalonia.Build.Tasks.UnitTest.csproj", "{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks.UnitTest", "tests\Avalonia.Build.Tasks.UnitTest\Avalonia.Build.Tasks.UnitTest.csproj", "{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestFiles", "TestFiles", "{9D6AEF22-221F-4F4B-B335-A4BA510F002C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildTasks", "BuildTasks", "{5BF0C3B8-E595-4940-AB30-2DA206C2F085}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PInvoke", "tests\TestFiles\BuildTasks\PInvoke\PInvoke.csproj", "{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PInvoke", "tests\TestFiles\BuildTasks\PInvoke\PInvoke.csproj", "{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}" EndProject - Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnloadableAssemblyLoadContext", "UnloadableAssemblyLoadContext", "{9CCA131B-DE95-4D44-8788-C3CAE28574CD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnloadableAssemblyLoadContext", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContext.csproj", "{D7FE3E0F-3FE0-4F87-A2F5-24F1454D84C0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnloadableAssemblyLoadContext", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContext.csproj", "{D7FE3E0F-3FE0-4F87-A2F5-24F1454D84C0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnloadableAssemblyLoadContextPlug", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContextPlug\UnloadableAssemblyLoadContextPlug.csproj", "{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnloadableAssemblyLoadContextPlug", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContextPlug\UnloadableAssemblyLoadContextPlug.csproj", "{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Vulkan", "src\Avalonia.Vulkan\Avalonia.Vulkan.csproj", "{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Vulkan", "src\Avalonia.Vulkan\Avalonia.Vulkan.csproj", "{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.RenderTests.WpfCompare", "tests\Avalonia.RenderTests.WpfCompare\Avalonia.RenderTests.WpfCompare.csproj", "{9AE1B827-21AC-4063-AB22-C8804B7F931E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.RenderTests.WpfCompare", "tests\Avalonia.RenderTests.WpfCompare\Avalonia.RenderTests.WpfCompare.csproj", "{9AE1B827-21AC-4063-AB22-C8804B7F931E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -714,10 +714,6 @@ Global {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.Build.0 = Release|Any CPU - {3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.Build.0 = Release|Any CPU {B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.Build.0 = Debug|Any CPU {B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -734,6 +730,10 @@ Global {DA5F1FF9-4259-4C54-B443-85CFA226EE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU {DA5F1FF9-4259-4C54-B443-85CFA226EE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA5F1FF9-4259-4C54-B443-85CFA226EE6A}.Release|Any CPU.Build.0 = Release|Any CPU + {3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.Build.0 = Release|Any CPU {9AE1B827-21AC-4063-AB22-C8804B7F931E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9AE1B827-21AC-4063-AB22-C8804B7F931E}.Debug|Any CPU.Build.0 = Debug|Any CPU {9AE1B827-21AC-4063-AB22-C8804B7F931E}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Avalonia.sln.DotSettings b/Avalonia.sln.DotSettings index cc58566622a..061db721683 100644 --- a/Avalonia.sln.DotSettings +++ b/Avalonia.sln.DotSettings @@ -36,7 +36,20 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" /> <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="s_" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Type parameters"><ElementKinds><Kind Name="TYPE_PARAMETER" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local variables"><ElementKinds><Kind Name="LOCAL_VARIABLE" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Parameters"><ElementKinds><Kind Name="PARAMETER" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Enum members"><ElementKinds><Kind Name="ENUM_MEMBER" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Interfaces"><ElementKinds><Kind Name="INTERFACE" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="I" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="s_" Suffix="" Style="aaBb" /></Policy> True + True True True True 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 a07532412d6..5dd69e7009b 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 @@ -17,7 +17,6 @@ 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 */; }; @@ -61,10 +60,11 @@ ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */; }; ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */; }; EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */; }; + F10084842BFF1F9E0024303E /* TopLevelImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = F10084832BFF1F9E0024303E /* TopLevelImpl.h */; }; + F10084862BFF1FB40024303E /* TopLevelImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = F10084852BFF1FB40024303E /* TopLevelImpl.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 = ""; }; @@ -125,6 +125,8 @@ ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; }; ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformRenderTimer.mm; sourceTree = ""; }; EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformSettings.mm; sourceTree = ""; }; + F10084832BFF1F9E0024303E /* TopLevelImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopLevelImpl.h; sourceTree = ""; }; + F10084852BFF1FB40024303E /* TopLevelImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TopLevelImpl.mm; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -164,9 +166,11 @@ AB7A61E62147C814003C5833 = { isa = PBXGroup; children = ( + F10084852BFF1FB40024303E /* TopLevelImpl.mm */, ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */, 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */, 8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */, + F10084832BFF1F9E0024303E /* TopLevelImpl.h */, 8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */, 8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */, BC11A5BC2608D58F0017BAD0 /* automation.h */, @@ -214,7 +218,6 @@ 1839155B28B20FFB672D29C6 /* AvnWindow.mm */, 18391DB45C7D892E61BF388C /* WindowInterfaces.h */, 18391BB698579F40F1783F31 /* PopupImpl.mm */, - 183910513F396141938832B5 /* PopupImpl.h */, 64B1EBEECBE13D8616D7C934 /* metal.mm */, 64B1E4FA7D9D6E5F47AA8606 /* noarc.mm */, 64B1E26F2B1B9C577BF52F06 /* noarc.h */, @@ -237,6 +240,7 @@ buildActionMask = 2147483647; files = ( 37155CE4233C00EB0034DCE9 /* menu.h in Headers */, + F10084842BFF1F9E0024303E /* TopLevelImpl.h in Headers */, BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */, 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */, 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */, @@ -249,7 +253,6 @@ 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */, 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */, - 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */, 64B1ECA861163C0EFF0E502B /* noarc.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -332,6 +335,7 @@ AB00E4F72147CA920032A60A /* main.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */, + F10084862BFF1FB40024303E /* TopLevelImpl.mm in Sources */, 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme index c1e5b91e3ef..5014c5d4394 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h index e058656d3f7..4f673fceefa 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.h +++ b/native/Avalonia.Native/src/OSX/AvnView.h @@ -7,20 +7,20 @@ #import #include "common.h" -#include "WindowImpl.h" +#include "TopLevelImpl.h" #include "KeyTransform.h" @class AvnAccessibilityElement; @protocol IRenderTarget; @interface AvnView : NSView --(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; +-(AvnView* _Nonnull) initWithParent: (TopLevelImpl* _Nonnull) parent; -(NSEvent* _Nonnull) lastMouseDownEvent; -(AvnPoint) translateLocalPoint:(AvnPoint)pt; -(void) onClosed; -(AvnPlatformResizeReason) getResizeReason; -(void) setResizeReason:(AvnPlatformResizeReason)reason; --(void) setRenderTarget:(NSObject*)target; +-(void) setRenderTarget:(NSObject* _Nonnull)target; + (AvnPoint)toAvnPoint:(CGPoint)p; @end diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index fc0d695384e..ab52bcc4a9c 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -7,10 +7,11 @@ #include "AvnView.h" #include "automation.h" #import "WindowInterfaces.h" +#import "WindowImpl.h" @implementation AvnView { - ComPtr _parent; + ComPtr _parent; NSTrackingArea* _area; bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; AvnInputModifiers _modifierState; @@ -67,7 +68,7 @@ -(void)displayLayer: (CALayer*)layer [self updateLayer]; } --(AvnView*) initWithParent: (WindowBaseImpl*) parent +-(AvnView*) initWithParent: (TopLevelImpl*) parent { self = [super init]; [self setWantsLayer:YES]; @@ -155,7 +156,7 @@ -(void)setFrameSize:(NSSize)newSize auto reason = [self inLiveResize] ? ResizeUser : _resizeReason; - _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason); + _parent->TopLevelEvents->Resized(FromNSSize(newSize), reason); } } @@ -167,14 +168,14 @@ - (void)updateLayer return; } - _parent->BaseEvents->RunRenderPriorityJobs(); + _parent->TopLevelEvents->RunRenderPriorityJobs(); if (_parent == nullptr) { return; } - _parent->BaseEvents->Paint(); + _parent->TopLevelEvents->Paint(); } - (void)drawRect:(NSRect)dirtyRect @@ -207,7 +208,7 @@ - (void) viewDidChangeBackingProperties if(_parent != nullptr) { - _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); + _parent->TopLevelEvents->ScalingChanged([[self window] backingScaleFactor]); } [super viewDidChangeBackingProperties]; @@ -219,19 +220,24 @@ - (bool) ignoreUserInput:(bool)trigerInputWhenDisabled { return TRUE; } + + id parentWindow = nullptr; - auto parentWindow = _parent->GetWindowProtocol(); + if([[self window] conformsToProtocol:@protocol(AvnWindowProtocol)]){ + parentWindow = (id)[self window]; + } - if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) + if(parentWindow != nullptr && ![parentWindow shouldTryToHandleEvents]) { if(trigerInputWhenDisabled) { - auto window = dynamic_cast(_parent.getRaw()); - - if(window != nullptr) - { - window->WindowEvents->GotInputWhenDisabled(); + WindowImpl* windowImpl = dynamic_cast(_parent.getRaw()); + + if(windowImpl == nullptr){ + return FALSE; } + + windowImpl->WindowEvents->GotInputWhenDisabled(); } return TRUE; @@ -301,7 +307,7 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type if(_parent != nullptr) { - _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); + _parent->TopLevelEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); } [super mouseMoved:event]; @@ -309,7 +315,7 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type - (BOOL) resignFirstResponder { - _parent->BaseEvents->LostFocus(); + _parent->TopLevelEvents->LostFocus(); return YES; } @@ -461,7 +467,7 @@ - (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type auto timestamp = static_cast([event timestamp] * 1000); auto modifiers = [self getModifiers:[event modifierFlags]]; - _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key, physicalKey, keySymbolUtf8); + _parent->TopLevelEvents->RawKeyEvent(type, timestamp, modifiers, key, physicalKey, keySymbolUtf8); } - (void)flagsChanged:(NSEvent *)event @@ -521,7 +527,7 @@ - (void)flagsChanged:(NSEvent *)event } - (bool) handleKeyDown: (NSTimeInterval) timestamp withKey:(AvnKey)key withPhysicalKey:(AvnPhysicalKey)physicalKey withModifiers:(AvnInputModifiers)modifiers withKeySymbol:(NSString*)keySymbol { - return _parent->BaseEvents->RawKeyEvent(KeyDown, timestamp, modifiers, key, physicalKey, [keySymbol UTF8String]); + return _parent->TopLevelEvents->RawKeyEvent(KeyDown, timestamp, modifiers, key, physicalKey, [keySymbol UTF8String]); } - (void)keyDown:(NSEvent *)event @@ -575,7 +581,7 @@ - (void)keyDown:(NSEvent *)event if(keySymbol != nullptr && key != AvnKeyEnter){ auto timestamp = static_cast([event timestamp] * 1000); - _parent->BaseEvents->RawTextInputEvent(timestamp, [keySymbol UTF8String]); + _parent->TopLevelEvents->RawTextInputEvent(timestamp, [keySymbol UTF8String]); } } } @@ -707,7 +713,7 @@ - (void)insertText:(id)string replacementRange:(NSRange)replacementRange uint64_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000); - _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]); + _parent->TopLevelEvents->RawTextInputEvent(timestamp, [text UTF8String]); } - (NSUInteger)characterIndexForPoint:(NSPoint)point @@ -733,7 +739,7 @@ - (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id BaseEvents + int reffects = (int)_parent->TopLevelEvents ->DragEvent(type, point, modifiers, effects, CreateClipboard([info draggingPasteboard], nil), GetAvnDataObjectHandleFromDraggingInfo(info)); @@ -799,7 +805,7 @@ - (AvnAccessibilityElement *) accessibilityChild { if (_accessibilityChild == nil) { - auto peer = _parent->BaseEvents->GetAutomationPeer(); + auto peer = _parent->TopLevelEvents->GetAutomationPeer(); if (peer == nil) return nil; diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index ef50cdab841..86d412c76a7 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -24,7 +24,6 @@ #include "WindowImpl.h" #include "AvnView.h" #include "WindowInterfaces.h" -#include "PopupImpl.h" @implementation CLASS_NAME { @@ -201,8 +200,6 @@ - (void)windowDidChangeBackingProperties:(NSNotification *_Nonnull)notification [self backingScaleFactor]; } - - - (void)windowWillClose:(NSNotification *_Nonnull)notification { _closed = true; @@ -231,7 +228,7 @@ - (void)windowWillClose:(NSNotification *_Nonnull)notification // // If we don't implement this, then isZoomed always returns true for a non- // resizable window ¯\_(ツ)_/¯ -- (NSRect)windowWillUseStandardFrame:(NSWindow*)window +- (NSRect)windowWillUseStandardFrame:(NSWindow* _Nonnull)window defaultFrame:(NSRect)newFrame { return newFrame; } @@ -397,7 +394,7 @@ - (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame return _parent->CanZoom(); } --(void)windowDidResignKey:(NSNotification *)notification +-(void)windowDidResignKey:(NSNotification* _Nonnull)notification { if(_parent) _parent->BaseEvents->Deactivated(); diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h index 3c5010966b8..7c07d6bb409 100644 --- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -11,6 +11,10 @@ struct INSWindowHolder { virtual NSWindow* _Nonnull GetNSWindow () = 0; +}; + +struct INSViewHolder +{ virtual AvnView* _Nonnull GetNSView () = 0; }; diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.h b/native/Avalonia.Native/src/OSX/PopupImpl.h deleted file mode 100644 index 451019a6a4f..00000000000 --- a/native/Avalonia.Native/src/OSX/PopupImpl.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// 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 index 39aa568134e..40fe8ce88b3 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -12,7 +12,6 @@ #import "WindowBaseImpl.h" #import "WindowProtocol.h" #import -#include "PopupImpl.h" class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup { @@ -23,7 +22,7 @@ END_INTERFACE_MAP() virtual ~PopupImpl(){} ComPtr WindowEvents; - PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events) + PopupImpl(IAvnWindowEvents* events) : TopLevelImpl(events), WindowBaseImpl(events) { WindowEvents = events; [Window setLevel:NSPopUpMenuWindowLevel]; @@ -35,13 +34,12 @@ virtual NSWindowStyleMask CalculateStyleMask() override } public: - virtual bool ShouldTakeFocusOnShow() override - { - return false; - } - virtual HRESULT Show(bool activate, bool isDialog) override { + auto windowProtocol = GetWindowProtocol(); + + [windowProtocol setEnabled:true]; + return WindowBaseImpl::Show(activate, true); } }; diff --git a/native/Avalonia.Native/src/OSX/TopLevelImpl.h b/native/Avalonia.Native/src/OSX/TopLevelImpl.h new file mode 100644 index 00000000000..d6243b62681 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/TopLevelImpl.h @@ -0,0 +1,76 @@ +// +// TopLevelImpl.h +// Avalonia.Native.OSX +// +// Created by Benedikt Stebner on 16.05.24. +// Copyright © 2024 Avalonia. All rights reserved. +// + +#ifndef TopLevelImpl_h +#define TopLevelImpl_h + +#include "rendertarget.h" +#include "INSWindowHolder.h" +#include "AvnTextInputMethod.h" +#include "AutoFitContentView.h" +#include + +class TopLevelImpl : public virtual ComObject, + public virtual IAvnTopLevel, + public INSViewHolder{ + +public: + FORWARD_IUNKNOWN() + BEGIN_INTERFACE_MAP() + INTERFACE_MAP_ENTRY(IAvnTopLevel, IID_IAvnTopLevel) + END_INTERFACE_MAP() + + virtual ~TopLevelImpl(); + + TopLevelImpl(IAvnTopLevelEvents* events); + + virtual AvnView *GetNSView() override; + + virtual HRESULT SetCursor(IAvnCursor* cursor) override; + + virtual HRESULT GetScaling(double*ret) override; + + virtual HRESULT GetClientSize(AvnSize *ret) override; + + virtual HRESULT GetInputMethod(IAvnTextInputMethod **ppv) override; + + virtual HRESULT ObtainNSViewHandle(void** retOut) override; + + virtual HRESULT ObtainNSViewHandleRetained(void** retOut) override; + + virtual HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget** ret) override; + + virtual HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget** ret) override; + + virtual HRESULT CreateGlRenderTarget(IAvnGlContext* context, IAvnGlSurfaceRenderTarget** ret) override; + + virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override; + + virtual HRESULT Invalidate() override; + + virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override; + + virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override; + + virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override; + +protected: + NSCursor *cursor; + virtual void UpdateAppearance(); + +public: + NSObject *currentRenderTarget; + ComPtr InputMethod; + ComPtr TopLevelEvents; + AvnView *View; + + void UpdateCursor(); + virtual void SetClientSize(NSSize size); +}; + +#endif /* TopLevelImpl_h */ diff --git a/native/Avalonia.Native/src/OSX/TopLevelImpl.mm b/native/Avalonia.Native/src/OSX/TopLevelImpl.mm new file mode 100644 index 00000000000..0a56b71f822 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/TopLevelImpl.mm @@ -0,0 +1,251 @@ +#import +#import +#include "automation.h" +#include "cursor.h" +#include "AutoFitContentView.h" +#include "TopLevelImpl.h" +#include "AvnTextInputMethod.h" +#include "AvnView.h" + +TopLevelImpl::~TopLevelImpl() { + View = nullptr; +} + +TopLevelImpl::TopLevelImpl(IAvnTopLevelEvents *events) { + TopLevelEvents = events; + + View = [[AvnView alloc] initWithParent:this]; + InputMethod = new AvnTextInputMethod(View); +} + +HRESULT TopLevelImpl::GetScaling(double *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + if ([View window] == nullptr) { + *ret = 1; + return S_OK; + } + + *ret = [[View window] backingScaleFactor]; + + return S_OK; + } +} + +HRESULT TopLevelImpl::GetClientSize(AvnSize *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + NSRect frame = [View frame]; + + ret->Width = frame.size.width; + ret->Height = frame.size.height; + + return S_OK; + } +} + +HRESULT TopLevelImpl::GetInputMethod(IAvnTextInputMethod **retOut) { + START_COM_CALL; + + *retOut = InputMethod; + + return S_OK; +} + +HRESULT TopLevelImpl::ObtainNSViewHandle(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge void *) View; + + return S_OK; +} + +HRESULT TopLevelImpl::ObtainNSViewHandleRetained(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge_retained void *) View; + + return S_OK; +} + +HRESULT TopLevelImpl::SetCursor(IAvnCursor *cursor) { + START_COM_CALL; + + @autoreleasepool { + Cursor *avnCursor = dynamic_cast(cursor); + this->cursor = avnCursor->GetNative(); + UpdateCursor(); + + if (avnCursor->IsHidden()) { + [NSCursor hide]; + } else { + [NSCursor unhide]; + } + + return S_OK; + } +} + +void TopLevelImpl::UpdateCursor() { + if (cursor != nil) { + [cursor set]; + } +} + +HRESULT TopLevelImpl::CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) { + START_COM_CALL; + + if(![NSThread isMainThread]) + return COR_E_INVALIDOPERATION; + + if (View == NULL) + return E_FAIL; + + auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: nil]; + *ppv = [target createSoftwareRenderTarget]; + [View setRenderTarget: target]; + return S_OK; +} + +HRESULT TopLevelImpl::CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) { + START_COM_CALL; + + if(![NSThread isMainThread]) + return COR_E_INVALIDOPERATION; + + if (View == NULL) + return E_FAIL; + + auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: glContext]; + *ppv = [target createSurfaceRenderTarget]; + [View setRenderTarget: target]; + return S_OK; +} + +HRESULT TopLevelImpl::CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) { + START_COM_CALL; + + if(![NSThread isMainThread]) + return COR_E_INVALIDOPERATION; + + if (View == NULL) + return E_FAIL; + + auto target = [[MetalRenderTarget alloc] initWithDevice: device]; + [View setRenderTarget: target]; + [target getRenderTarget: ppv]; + return S_OK; +} + +HRESULT TopLevelImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { + START_COM_CALL; + + if (View == NULL) + return E_FAIL; + *retOut = ::CreateNativeControlHost(View); + return S_OK; +} + +AvnView *TopLevelImpl::GetNSView() { + return View; +} + +HRESULT TopLevelImpl::Invalidate() { + START_COM_CALL; + + @autoreleasepool { + [View setNeedsDisplayInRect:[View frame]]; + + return S_OK; + } +} + +HRESULT TopLevelImpl::PointToClient(AvnPoint point, AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + auto window = [View window]; + + if(window == nullptr){ + ret = &point; + + return S_OK; + } + + 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 TopLevelImpl::PointToScreen(AvnPoint point, AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + auto window = [View window]; + + if(window == nullptr){ + ret = &point; + + return S_OK; + } + + 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 TopLevelImpl::SetTransparencyMode(AvnWindowTransparencyMode mode) { + START_COM_CALL; + + return S_OK; +} + +void TopLevelImpl::UpdateAppearance() { + +} + +void TopLevelImpl::SetClientSize(NSSize size){ + [View setFrameSize:size]; +} + +extern IAvnTopLevel* CreateAvnTopLevel(IAvnTopLevelEvents* events) +{ + @autoreleasepool + { + IAvnTopLevel* ptr = (IAvnTopLevel*)new TopLevelImpl(events); + return ptr; + } +} diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 83c7aed5d38..ab0a5fb6a19 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -9,19 +9,21 @@ #include "rendertarget.h" #include "INSWindowHolder.h" #include "AvnTextInputMethod.h" +#include "TopLevelImpl.h" +#include -@class AutoFitContentView; @class AvnMenu; @protocol AvnWindowProtocol; -class WindowBaseImpl : public virtual ComObject, +class WindowBaseImpl : public virtual TopLevelImpl, public virtual IAvnWindowBase, public INSWindowHolder { public: FORWARD_IUNKNOWN() -BEGIN_INTERFACE_MAP() + BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(TopLevelImpl) INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) END_INTERFACE_MAP() @@ -33,14 +35,8 @@ BEGIN_INTERFACE_MAP() virtual HRESULT ObtainNSWindowHandleRetained(void **ret) override; - virtual HRESULT ObtainNSViewHandle(void **ret) override; - - virtual HRESULT ObtainNSViewHandleRetained(void **ret) override; - virtual NSWindow *GetNSWindow() override; - virtual AvnView *GetNSView() override; - virtual HRESULT Show(bool activate, bool isDialog) override; virtual bool IsShown (); @@ -55,18 +51,12 @@ BEGIN_INTERFACE_MAP() 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; @@ -77,49 +67,33 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetPosition(AvnPoint point) override; - virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override; - - virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override; - - virtual HRESULT SetCursor(IAvnCursor *cursor) override; - - virtual void UpdateCursor(); - - virtual HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) override; - - virtual HRESULT CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) override; - - virtual HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) override; - - virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override; - - virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override; - virtual HRESULT SetFrameThemeVariant(AvnPlatformThemeVariant variant) override; virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) override; + virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override; + virtual bool IsModal(); id GetWindowProtocol (); virtual void BringToFront (); - - virtual HRESULT GetInputMethod(IAvnTextInputMethod **retOut) override; virtual bool CanZoom() { return false; } + virtual HRESULT SetParent(IAvnWindowBase* parent) override; + protected: virtual NSWindowStyleMask CalculateStyleMask() = 0; - virtual void UpdateStyle(); + virtual void UpdateAppearance() override; + virtual void SetClientSize(NSSize size) override; private: void CreateNSWindow (bool isDialog); void CleanNSWindow (); - NSCursor *cursor; bool hasPosition; NSSize lastSize; NSSize lastMinSize; @@ -128,16 +102,16 @@ BEGIN_INTERFACE_MAP() bool _inResize; protected: - AvnPoint lastPositionSet; AutoFitContentView *StandardContainer; + AvnPoint lastPositionSet; bool _shown; + std::list _children; + bool _isModal; public: - NSObject *currentRenderTarget; + WindowBaseImpl* Parent; NSWindow * Window; ComPtr BaseEvents; - ComPtr InputMethod; - AvnView *View; }; #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 3f54680ae77..afae6d6b5bc 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -15,22 +15,22 @@ #import "WindowProtocol.h" #import "WindowInterfaces.h" #include "WindowBaseImpl.h" +#include "WindowImpl.h" #include "AvnTextInputMethod.h" #include "AvnView.h" +@class AutoFitContentView; WindowBaseImpl::~WindowBaseImpl() { View = nullptr; Window = nullptr; } -WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) { +WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) : TopLevelImpl(events) { + _children = std::list(); _shown = false; _inResize = false; BaseEvents = events; - View = [[AvnView alloc] initWithParent:this]; - InputMethod = new AvnTextInputMethod(View); - StandardContainer = [[AutoFitContentView new] initWithContent:View]; lastPositionSet = { 0, 0 }; hasPosition = false; @@ -41,6 +41,8 @@ CreateNSWindow(usePanel); + StandardContainer = [[AutoFitContentView new] initWithContent:View]; + [Window setContentView:StandardContainer]; [Window setBackingType:NSBackingStoreBuffered]; [Window setContentMinSize:lastMinSize]; @@ -48,38 +50,10 @@ [Window setOpaque:false]; } -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; } -AvnView *WindowBaseImpl::GetNSView() { - return View; -} - HRESULT WindowBaseImpl::ObtainNSWindowHandleRetained(void **ret) { START_COM_CALL; @@ -117,7 +91,7 @@ auto collectionBehavior = [Window collectionBehavior]; [Window setCollectionBehavior:collectionBehavior & ~NSWindowCollectionBehaviorFullScreenPrimary]; - UpdateStyle(); + UpdateAppearance(); [Window invalidateShadow]; @@ -219,20 +193,6 @@ } } -HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) { - START_COM_CALL; - - @autoreleasepool { - if (ret == nullptr) - return E_POINTER; - - ret->Width = lastSize.width; - ret->Height = lastSize.height; - - return S_OK; - } -} - HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) { START_COM_CALL; @@ -250,23 +210,6 @@ } } -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; @@ -330,7 +273,7 @@ lastSize = NSSize{x, y}; - [Window setContentSize:lastSize]; + SetClientSize(lastSize); [Window invalidateShadow]; } } @@ -342,16 +285,6 @@ } } -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; @@ -431,119 +364,6 @@ } } -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::SetCursor(IAvnCursor *cursor) { - START_COM_CALL; - - @autoreleasepool { - Cursor *avnCursor = dynamic_cast(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::CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) { - START_COM_CALL; - - if(![NSThread isMainThread]) - return COR_E_INVALIDOPERATION; - - if (View == NULL) - return E_FAIL; - - auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: nil]; - *ppv = [target createSoftwareRenderTarget]; - [View setRenderTarget: target]; - return S_OK; -} - -HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) { - START_COM_CALL; - - if(![NSThread isMainThread]) - return COR_E_INVALIDOPERATION; - - if (View == NULL) - return E_FAIL; - - auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: glContext]; - *ppv = [target createSurfaceRenderTarget]; - [View setRenderTarget: target]; - return S_OK; -} - -HRESULT WindowBaseImpl::CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) { - START_COM_CALL; - - if(![NSThread isMainThread]) - return COR_E_INVALIDOPERATION; - - if (View == NULL) - return E_FAIL; - - auto target = [[MetalRenderTarget alloc] initWithDevice: device]; - [View setRenderTarget: target]; - [target getRenderTarget: ppv]; - return S_OK; -} - -HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { - START_COM_CALL; - - if (View == NULL) - return E_FAIL; - *retOut = ::CreateNativeControlHost(View); - return S_OK; -} - HRESULT WindowBaseImpl::SetTransparencyMode(AvnWindowTransparencyMode mode) { START_COM_CALL; @@ -619,10 +439,14 @@ return false; } -void WindowBaseImpl::UpdateStyle() { +void WindowBaseImpl::UpdateAppearance() { [Window setStyleMask:CalculateStyleMask()]; } +void WindowBaseImpl::SetClientSize(NSSize size){ + [Window setContentSize:lastSize]; +} + void WindowBaseImpl::CleanNSWindow() { if(Window != nullptr) { [GetWindowProtocol() disconnectParent]; @@ -654,19 +478,35 @@ // do nothing. } -HRESULT WindowBaseImpl::GetInputMethod(IAvnTextInputMethod **retOut) { +HRESULT WindowBaseImpl::SetParent(IAvnWindowBase *parent) { START_COM_CALL; - *retOut = InputMethod; + @autoreleasepool { + if(Parent != nullptr) + { + Parent->_children.remove(this); + } + + auto cparent = dynamic_cast(parent); + + Parent = cparent; - return S_OK; -} + _isModal = Parent != nullptr; + + if(Parent != nullptr && Window != nullptr){ + // 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->_children.push_back(this); + + UpdateAppearance(); + } -extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events) -{ - @autoreleasepool - { - IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events); - return ptr; + return S_OK; } } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index 047a0d2c84d..b931e933db5 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -8,26 +8,9 @@ #import "WindowBaseImpl.h" #include "IWindowStateChanged.h" -#include - class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged { -private: - bool _isEnabled; - bool _canResize; - bool _fullScreenActive; - SystemDecorations _decorations; - AvnWindowState _lastWindowState; - AvnWindowState _actualWindowState; - bool _inSetWindowState; - NSRect _preZoomSize; - bool _transitioningWindowState; - bool _isClientAreaExtended; - bool _isModal; - WindowImpl* _parent; - std::list _children; - AvnExtendClientAreaChromeHints _extendClientHints; - +public: FORWARD_IUNKNOWN() BEGIN_INTERFACE_MAP() INHERIT_INTERFACE_MAP(WindowBaseImpl) @@ -45,8 +28,6 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetEnabled (bool enable) override; - virtual HRESULT SetParent (IAvnWindow* parent) override; - void StartStateTransition () override ; void EndStateTransition () override ; @@ -103,12 +84,23 @@ BEGIN_INTERFACE_MAP() protected: virtual NSWindowStyleMask CalculateStyleMask() override; - void UpdateStyle () override; + virtual void UpdateAppearance() override; private: void ZOrderChildWindows(); void OnInitialiseNSWindow(); NSString *_lastTitle; + bool _isEnabled; + bool _canResize; + bool _fullScreenActive; + SystemDecorations _decorations; + AvnWindowState _lastWindowState; + AvnWindowState _actualWindowState; + bool _inSetWindowState; + NSRect _preZoomSize; + bool _transitioningWindowState; + bool _isClientAreaExtended; + AvnExtendClientAreaChromeHints _extendClientHints; }; #endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 1cdf81e2fbe..42ac37ae8cc 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -8,10 +8,10 @@ #include "AvnView.h" #include "automation.h" #include "WindowProtocol.h" +#include "WindowImpl.h" -WindowImpl::WindowImpl(IAvnWindowEvents *events) : WindowBaseImpl(events) { +WindowImpl::WindowImpl(IAvnWindowEvents *events) : TopLevelImpl(events), WindowBaseImpl(events, false) { _isEnabled = true; - _children = std::list(); _isClientAreaExtended = false; _extendClientHints = AvnDefaultChrome; _fullScreenActive = false; @@ -22,7 +22,7 @@ _lastWindowState = Normal; _actualWindowState = Normal; _lastTitle = @""; - _parent = nullptr; + Parent = nullptr; WindowEvents = events; [Window setHasShadow:true]; @@ -69,40 +69,7 @@ @autoreleasepool { _isEnabled = enable; [GetWindowProtocol() setEnabled:enable]; - UpdateStyle(); - return S_OK; - } -} - -HRESULT WindowImpl::SetParent(IAvnWindow *parent) { - START_COM_CALL; - - @autoreleasepool { - if(_parent != nullptr) - { - _parent->_children.remove(this); - } - - auto cparent = dynamic_cast(parent); - - _parent = cparent; - - _isModal = _parent != nullptr; - - if(_parent != nullptr && Window != nullptr){ - // 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->_children.push_back(this); - - UpdateStyle(); - } - + UpdateAppearance(); return S_OK; } } @@ -156,12 +123,12 @@ void WindowImpl::StartStateTransition() { _transitioningWindowState = true; - UpdateStyle(); + UpdateAppearance(); } void WindowImpl::EndStateTransition() { _transitioningWindowState = false; - UpdateStyle(); + UpdateAppearance(); // Ensure correct order of child windows after fullscreen transition. ZOrderChildWindows(); @@ -236,7 +203,7 @@ @autoreleasepool { _canResize = value; - UpdateStyle(); + UpdateAppearance(); return S_OK; } } @@ -252,7 +219,7 @@ return S_OK; } - UpdateStyle(); + UpdateAppearance(); switch (_decorations) { case SystemDecorationsNone: @@ -427,7 +394,7 @@ } [GetWindowProtocol() setIsExtended:enable]; - UpdateStyle(); + UpdateAppearance(); } return S_OK; @@ -579,7 +546,7 @@ } bool WindowImpl::IsOwned() { - return _parent != nullptr; + return Parent != nullptr; } NSWindowStyleMask WindowImpl::CalculateStyleMask() { @@ -620,8 +587,8 @@ return s; } -void WindowImpl::UpdateStyle() { - WindowBaseImpl::UpdateStyle(); +void WindowImpl::UpdateAppearance() { + WindowBaseImpl::UpdateAppearance(); if (Window == nil) { return; @@ -642,3 +609,12 @@ [zoomButton setHidden:!hasTrafficLights]; [zoomButton setEnabled:CanZoom()]; } + +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events) +{ + @autoreleasepool + { + IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events); + return ptr; + } +} diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 9fe0ff3c60b..0f5f2943291 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -80,7 +80,7 @@ + (AvnAccessibilityElement *)acquire:(IAvnAutomationPeer *)peer return nil; } - auto holder = dynamic_cast(window); + auto holder = dynamic_cast(window); auto view = holder->GetNSView(); return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view]; } diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index a3473d39281..38b702d7754 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -11,6 +11,7 @@ extern IAvnPlatformThreadingInterface* CreatePlatformThreading(); extern void FreeAvnGCHandle(void* handle); extern void PostDispatcherCallback(IAvnActionCallback* cb); +extern IAvnTopLevel* CreateAvnTopLevel(IAvnTopLevelEvents* events); extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events); extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events); extern IAvnSystemDialogs* CreateSystemDialogs(); @@ -46,6 +47,7 @@ extern NSRect ToNSRect (AvnRect r); extern AvnPoint ToAvnPoint (NSPoint p); extern AvnPoint ConvertPointY (AvnPoint p); extern NSSize ToNSSize (AvnSize s); +extern AvnSize FromNSSize (NSSize s); #ifdef DEBUG #define NSDebugLog(...) NSLog(__VA_ARGS__) #else diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 41d6fd37abd..a36170fddc0 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -225,6 +225,19 @@ virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, return (IAvnMacOptions*)new MacOptions(); } + virtual HRESULT CreateTopLevel(IAvnTopLevelEvents* cb, + IAvnTopLevel** ppv) override { + START_COM_CALL; + + @autoreleasepool + { + if(cb == nullptr || ppv == nullptr) + return E_POINTER; + *ppv = CreateAvnTopLevel(cb); + return S_OK; + } + } + virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) override { START_COM_CALL; @@ -484,6 +497,15 @@ NSSize ToNSSize (AvnSize s) return result; } +AvnSize FromNSSize (NSSize s) +{ + AvnSize result; + result.Width = s.width; + result.Height = s.height; + + return result; +} + NSPoint ToNSPoint (AvnPoint p) { NSPoint result; diff --git a/nukebuild/_build.csproj.DotSettings b/nukebuild/_build.csproj.DotSettings index 9aac7d8e8d4..7348ae5acbb 100644 --- a/nukebuild/_build.csproj.DotSettings +++ b/nukebuild/_build.csproj.DotSettings @@ -13,6 +13,8 @@ False <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> True True True @@ -21,4 +23,5 @@ True True True - True + True + True diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 02e3f68d739..e3716d2a4e4 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -100,6 +100,8 @@ public TopLevelImpl(AvaloniaView avaloniaView, bool placeOnTop = false) internal InvalidationAwareSurfaceView InternalView => _view; + public double DesktopScaling => RenderScaling; + public IScreenImpl? Screen { get; } public IPlatformHandle Handle => _view; public IEnumerable Surfaces { get; } diff --git a/src/Android/Avalonia.Android/Stubs.cs b/src/Android/Avalonia.Android/Stubs.cs index 21df905bb68..c79a23d8e7a 100644 --- a/src/Android/Avalonia.Android/Stubs.cs +++ b/src/Android/Avalonia.Android/Stubs.cs @@ -7,6 +7,7 @@ namespace Avalonia.Android internal class WindowingPlatformStub : IWindowingPlatform { public IWindowImpl CreateWindow() => throw new NotSupportedException(); + public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow(); public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException(); diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index 9a2f98f771c..091098f2b01 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -13,7 +13,7 @@ public EmbeddableControlRoot(ITopLevelImpl impl) : base(impl) { } - public EmbeddableControlRoot() : base(PlatformManager.CreateEmbeddableWindow()) + public EmbeddableControlRoot() : base(PlatformManager.CreateEmbeddableTopLevel()) { } diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index f1c388504df..f631b18c04a 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -29,6 +29,10 @@ public OffscreenTopLevelImplBase() public abstract IEnumerable Surfaces { get; } + public double DesktopScaling => _scaling; + public IScreenImpl? Screen { get; } + public IPlatformHandle? Handle { get; } + public Size ClientSize { get => _clientSize; diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 35478053c4a..3cf72018616 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -3,9 +3,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Raw; -using Avalonia.Layout; using Avalonia.Metadata; -using Avalonia.Rendering; using Avalonia.Rendering.Composition; namespace Avalonia.Platform @@ -21,14 +19,24 @@ namespace Avalonia.Platform public interface ITopLevelImpl : IOptionalFeatureProvider, IDisposable { /// - /// Gets the client size of the toplevel. + /// Gets the scaling factor for Window positioning and sizing. /// - Size ClientSize { get; } + double DesktopScaling { get; } + + /// + /// Gets platform specific display information + /// + IScreenImpl? Screen { get; } /// - /// Gets the total size of the toplevel, excluding shadows. + /// Get the platform handle. /// - Size? FrameSize { get; } + IPlatformHandle? Handle { get; } + + /// + /// Gets the client size of the toplevel. + /// + Size ClientSize { get; } /// /// Gets the scaling factor for the toplevel. This is used for rendering. @@ -123,16 +131,16 @@ public interface ITopLevelImpl : IOptionalFeatureProvider, IDisposable /// Gets the current of the TopLevel. /// WindowTransparencyLevel TransparencyLevel { get; } + + /// + /// Gets the for the platform. + /// + AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } /// /// Sets the on the frame if it should be dark or light. /// Also applies for the mobile status bar. /// void SetFrameThemeVariant(PlatformThemeVariant themeVariant); - - /// - /// Gets the for the platform. - /// - AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } } } diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index 512fad6dfce..ec902ff7c3b 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -1,5 +1,4 @@ using System; -using Avalonia.Automation.Peers; using Avalonia.Metadata; namespace Avalonia.Platform @@ -7,6 +6,11 @@ namespace Avalonia.Platform [Unstable] public interface IWindowBaseImpl : ITopLevelImpl { + /// + /// Gets the total size of the toplevel, excluding shadows. + /// + Size? FrameSize { get; } + /// /// Shows the window. /// @@ -18,11 +22,6 @@ public interface IWindowBaseImpl : ITopLevelImpl /// Hides the window. /// void Hide(); - - /// - /// Gets the scaling factor for Window positioning and sizing. - /// - double DesktopScaling { get; } /// /// Gets the position of the window in device pixels. @@ -48,11 +47,6 @@ public interface IWindowBaseImpl : ITopLevelImpl /// Gets or sets a method called when the window is activated (receives focus). /// Action? Activated { get; set; } - - /// - /// Gets the platform window handle. - /// - IPlatformHandle Handle { get; } /// /// Gets a maximum client size hint for an auto-sizing window, in device-independent pixels. @@ -63,10 +57,5 @@ public interface IWindowBaseImpl : ITopLevelImpl /// Sets whether this window appears on top of all other windows /// void SetTopmost(bool value); - - /// - /// Gets platform specific display information - /// - IScreenImpl Screen { get; } } } diff --git a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs index f6cf8c604e0..09f490d50a2 100644 --- a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs +++ b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs @@ -7,6 +7,8 @@ public interface IWindowingPlatform { IWindowImpl CreateWindow(); + ITopLevelImpl CreateEmbeddableTopLevel(); + IWindowImpl CreateEmbeddableWindow(); ITrayIconImpl? CreateTrayIcon(); diff --git a/src/Avalonia.Controls/Platform/PlatformManager.cs b/src/Avalonia.Controls/Platform/PlatformManager.cs index 6f043463d40..2a8d49397f9 100644 --- a/src/Avalonia.Controls/Platform/PlatformManager.cs +++ b/src/Avalonia.Controls/Platform/PlatformManager.cs @@ -36,5 +36,11 @@ public static IWindowImpl CreateEmbeddableWindow() var platform = AvaloniaLocator.Current.GetRequiredService(); return platform.CreateEmbeddableWindow(); } + + public static ITopLevelImpl CreateEmbeddableTopLevel() + { + var platform = AvaloniaLocator.Current.GetRequiredService(); + return platform.CreateEmbeddableTopLevel(); + } } } diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs index 41a2a98fd35..83d157737e9 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs @@ -12,22 +12,33 @@ namespace Avalonia.Controls.Primitives.PopupPositioning [PrivateApi] public class ManagedPopupPositionerPopupImplHelper : IManagedPopupPositionerPopup { - private readonly IWindowBaseImpl _parent; + private readonly ITopLevelImpl _parent; public delegate void MoveResizeDelegate(PixelPoint position, Size size, double scaling); private readonly MoveResizeDelegate _moveResize; - public ManagedPopupPositionerPopupImplHelper(IWindowBaseImpl parent, MoveResizeDelegate moveResize) + public ManagedPopupPositionerPopupImplHelper(ITopLevelImpl parent, MoveResizeDelegate moveResize) { _parent = parent; _moveResize = moveResize; } - public IReadOnlyList Screens => + public IReadOnlyList Screens + { + get + { + if (_parent.Screen is null) + { + return Array.Empty(); + } + + return _parent.Screen.AllScreens + .Select(s => new ManagedPopupPositionerScreenInfo(s.Bounds.ToRect(1), s.WorkingArea.ToRect(1))) + .ToArray(); + } + } - _parent.Screen.AllScreens - .Select(s => new ManagedPopupPositionerScreenInfo(s.Bounds.ToRect(1), s.WorkingArea.ToRect(1))) - .ToArray(); + public Rect ParentClientAreaScreenGeometry { diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 53821c7a5c5..b9b0c29523b 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -232,11 +232,6 @@ public TopLevel(ITopLevelImpl impl, IAvaloniaDependencyResolver? dependencyResol impl.TransparencyLevelChanged = HandleTransparencyLevelChanged; CreatePlatformImplBinding(TransparencyLevelHintProperty, hint => PlatformImpl.SetTransparencyLevelHint(hint ?? Array.Empty())); - CreatePlatformImplBinding(ActualThemeVariantProperty, variant => - { - variant ??= ThemeVariant.Default; - PlatformImpl.SetFrameThemeVariant((PlatformThemeVariant?)variant ?? PlatformThemeVariant.Light); - }); _keyboardNavigationHandler?.SetOwner(this); _accessKeyHandler?.SetOwner(this); @@ -253,7 +248,6 @@ public TopLevel(ITopLevelImpl impl, IAvaloniaDependencyResolver? dependencyResol } ClientSize = impl.ClientSize; - FrameSize = impl.FrameSize; if (((IStyleHost)this).StylingParent is IResourceHost applicationResources) { @@ -429,7 +423,7 @@ internal ILayoutManager LayoutManager /// An describing the window handle, or null if the handle /// could not be retrieved. /// - public IPlatformHandle? TryGetPlatformHandle() => (PlatformImpl as IWindowBaseImpl)?.Handle; + public IPlatformHandle? TryGetPlatformHandle() => PlatformImpl?.Handle; private protected void CreatePlatformImplBinding(StyledProperty property, Action onValue) { @@ -728,7 +722,6 @@ private protected virtual void HandleClosed() internal virtual void HandleResized(Size clientSize, WindowResizeReason reason) { ClientSize = clientSize; - FrameSize = PlatformImpl!.FrameSize; Width = clientSize.Width; Height = clientSize.Height; LayoutManager.ExecuteLayoutPass(); @@ -789,7 +782,6 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) /// The event args. protected virtual void OnOpened(EventArgs e) { - FrameSize = PlatformImpl?.FrameSize; Dispatcher.UIThread.Send(_ => Opened?.Invoke(this, e)); } diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 73a696695df..6715c8e4bc3 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; +using Avalonia.Styling; namespace Avalonia.Controls { @@ -47,11 +48,18 @@ static WindowBase() public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current) { CreatePlatformImplBinding(TopmostProperty, topmost => PlatformImpl!.SetTopmost(topmost)); + CreatePlatformImplBinding(ActualThemeVariantProperty, variant => + { + variant ??= ThemeVariant.Default; + PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)variant ?? PlatformThemeVariant.Light); + }); + + FrameSize = impl.FrameSize; } public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver? dependencyResolver) : base(impl, dependencyResolver) { - Screens = new Screens(impl.Screen); + Screens = new Screens(impl.Screen!); impl.Activated = HandleActivated; impl.Deactivated = HandleDeactivated; impl.PositionChanged = HandlePositionChanged; @@ -212,6 +220,8 @@ protected override void OnClosed(EventArgs e) /// protected override void OnOpened(EventArgs e) { + FrameSize = PlatformImpl?.FrameSize; + // Window must manually raise Loaded/Unloaded events as it is a visual root and // does not raise OnAttachedToVisualTreeCore/OnDetachedFromVisualTreeCore events ScheduleOnLoadedCore(); diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs index ba9dd592ced..301f8a7284c 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs @@ -21,6 +21,7 @@ class PreviewerWindowingPlatform : IWindowingPlatform public ITrayIconImpl CreateTrayIcon() => null; public IWindowImpl CreateWindow() => new WindowStub(); + public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow(); public IWindowImpl CreateEmbeddableWindow() { diff --git a/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs b/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs index c75f1b19aec..b1caa3b757a 100644 --- a/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs +++ b/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs @@ -190,10 +190,10 @@ public void Dispose() class GlPlatformSurface : IGlPlatformSurface { - private readonly IAvnWindowBase _window; - public GlPlatformSurface(IAvnWindowBase window) + private readonly IAvnTopLevel _topLevel; + public GlPlatformSurface(IAvnTopLevel topLevel) { - _window = window; + _topLevel = topLevel; } public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context) @@ -201,7 +201,7 @@ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context) if (!Dispatcher.UIThread.CheckAccess()) throw new RenderTargetNotReadyException(); var avnContext = (GlContext)context; - return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget(avnContext.Context), avnContext); + return new GlPlatformSurfaceRenderTarget(_topLevel.CreateGlRenderTarget(avnContext.Context), avnContext); } } diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index d8ab8363b58..d50965def77 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -29,7 +29,12 @@ class AvaloniaNativePlatform : IWindowingPlatform public static AvaloniaNativePlatform Initialize(IntPtr factory, AvaloniaNativePlatformOptions options) { - var result = new AvaloniaNativePlatform(MicroComRuntime.CreateProxyFor(factory, true)); + var factoryProxy = MicroComRuntime.CreateProxyFor(factory, true); + + AvaloniaLocator.CurrentMutable.Bind().ToConstant(factoryProxy); + + var result = new AvaloniaNativePlatform(factoryProxy); + result.DoInitialize(options); return result; @@ -187,5 +192,10 @@ public IWindowImpl CreateEmbeddableWindow() { throw new NotImplementedException(); } + + public ITopLevelImpl CreateEmbeddableTopLevel() + { + return new EmbeddableTopLevelImpl(_factory); + } } } diff --git a/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs b/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs index 0171ada7b9f..a3abccd2b40 100644 --- a/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs +++ b/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs @@ -13,9 +13,9 @@ internal class AvaloniaNativeTextInputMethod : ITextInputMethodImpl, IDisposable private IAvnTextInputMethodClient? _nativeClient; private readonly IAvnTextInputMethod _inputMethod; - public AvaloniaNativeTextInputMethod(IAvnWindowBase nativeWindow) + public AvaloniaNativeTextInputMethod(IAvnTopLevel topLevel) { - _inputMethod = nativeWindow.InputMethod; + _inputMethod = topLevel.InputMethod; } public void Dispose() diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs index 0942bee508a..fd3f3ce6767 100644 --- a/src/Avalonia.Native/DeferredFramebuffer.cs +++ b/src/Avalonia.Native/DeferredFramebuffer.cs @@ -9,13 +9,13 @@ namespace Avalonia.Native internal unsafe class DeferredFramebuffer : ILockedFramebuffer { private readonly IAvnSoftwareRenderTarget _renderTarget; - private readonly Action> _lockWindow; + private readonly Action> _lockTopLevel; - public DeferredFramebuffer(IAvnSoftwareRenderTarget renderTarget, Action> lockWindow, + public DeferredFramebuffer(IAvnSoftwareRenderTarget renderTarget, Action> lockTopLevel, int width, int height, Vector dpi) { _renderTarget = renderTarget; - _lockWindow = lockWindow; + _lockTopLevel = lockTopLevel; Address = Marshal.AllocHGlobal(width * height * 4); Size = new PixelSize(width, height); RowBytes = width * 4; @@ -35,7 +35,7 @@ public void Dispose() if (Address == IntPtr.Zero) return; - _lockWindow(win => + _lockTopLevel(win => { var fb = new AvnFramebuffer { diff --git a/src/Avalonia.Native/EmbeddableTopLevelImpl.cs b/src/Avalonia.Native/EmbeddableTopLevelImpl.cs new file mode 100644 index 00000000000..34f955ca067 --- /dev/null +++ b/src/Avalonia.Native/EmbeddableTopLevelImpl.cs @@ -0,0 +1,15 @@ +using Avalonia.Native.Interop; + +namespace Avalonia.Native +{ + internal class EmbeddableTopLevelImpl : TopLevelImpl + { + public EmbeddableTopLevelImpl(IAvaloniaNativeFactory factory) : base(factory) + { + using (var e = new TopLevelEvents(this)) + { + Init(new MacOSTopLevelHandle(factory.CreateTopLevel(e)), factory.CreateScreens()); + } + } + } +} diff --git a/src/Avalonia.Native/Metal.cs b/src/Avalonia.Native/Metal.cs index 8c8d2935769..ef7a1f7d851 100644 --- a/src/Avalonia.Native/Metal.cs +++ b/src/Avalonia.Native/Metal.cs @@ -50,11 +50,11 @@ public void Dispose() class MetalPlatformSurface : IMetalPlatformSurface { - private readonly IAvnWindowBase _window; + private readonly IAvnTopLevel _topLevel; - public MetalPlatformSurface(IAvnWindowBase window) + public MetalPlatformSurface(IAvnTopLevel topLevel) { - _window = window; + _topLevel = topLevel; } public IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice device) { @@ -62,7 +62,7 @@ public IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice de throw new RenderTargetNotReadyException(); var dev = (MetalDevice)device; - var target = _window.CreateMetalRenderTarget(dev.Native); + var target = _topLevel.CreateMetalRenderTarget(dev.Native); return new MetalRenderTarget(target); } } diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs index 2dad0837674..e0d7a0a3ebb 100644 --- a/src/Avalonia.Native/PopupImpl.cs +++ b/src/Avalonia.Native/PopupImpl.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Native.Interop; using Avalonia.Platform; @@ -8,17 +7,37 @@ namespace Avalonia.Native { class PopupImpl : WindowBaseImpl, IPopupImpl { - private readonly IWindowBaseImpl _parent; + private readonly ITopLevelImpl _parent; + private readonly IAvnPopup _native; + private readonly AvaloniaNativeTextInputMethod _inputMethod; public PopupImpl(IAvaloniaNativeFactory factory, - IWindowBaseImpl parent) : base(factory) + ITopLevelImpl parent) : base(factory) { _parent = parent; + using (var e = new PopupEvents(this)) { - Init(factory.CreatePopup(e), factory.CreateScreens()); + Init(new MacOSTopLevelHandle(_native = factory.CreatePopup(e)), factory.CreateScreens()); } + PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize)); + + while (parent is PopupImpl popupImpl) + { + parent = popupImpl._parent; + } + + //Use the parent's input context to process events + if (parent is TopLevelImpl topLevelImpl) + { + _inputMethod = topLevelImpl.InputMethod; + } + } + + internal sealed override void Init(MacOSTopLevelHandle handle, IAvnScreens screens) + { + base.Init(handle, screens); } private void MoveResize(PixelPoint position, Size size, double scaling) @@ -62,7 +81,7 @@ public override void Show(bool activate, bool isDialog) base.Show(false, isDialog); } - public override IPopupImpl CreatePopup() => new PopupImpl(_factory, this); + public override IPopupImpl CreatePopup() => new PopupImpl(Factory, this); public void SetWindowManagerAddShadowHint(bool enabled) { diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs index 76bf2d3bfa3..bf898178814 100644 --- a/src/Avalonia.Native/SystemDialogs.cs +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -14,12 +14,12 @@ namespace Avalonia.Native { internal class SystemDialogs : BclStorageProvider { - private readonly WindowBaseImpl _window; + private readonly TopLevelImpl _topLevel; private readonly IAvnSystemDialogs _native; - public SystemDialogs(WindowBaseImpl window, IAvnSystemDialogs native) + public SystemDialogs(TopLevelImpl topLevel, IAvnSystemDialogs native) { - _window = window; + _topLevel = topLevel; _native = native; } @@ -36,7 +36,7 @@ public override async Task> OpenFilePickerAsync(File var suggestedDirectory = options.SuggestedStartLocation?.TryGetLocalPath() ?? string.Empty; - _native.OpenFileDialog((IAvnWindow)_window.Native, + _native.OpenFileDialog((IAvnWindow)_topLevel.Native, events, options.AllowMultiple.AsComBool(), options.Title ?? string.Empty, @@ -57,7 +57,7 @@ public override async Task> OpenFilePickerAsync(File var suggestedDirectory = options.SuggestedStartLocation?.TryGetLocalPath() ?? string.Empty; - _native.SaveFileDialog((IAvnWindow)_window.Native, + _native.SaveFileDialog((IAvnWindow)_topLevel.Native, events, options.Title ?? string.Empty, suggestedDirectory, @@ -76,7 +76,7 @@ public override async Task> OpenFolderPickerAsync( var suggestedDirectory = options.SuggestedStartLocation?.TryGetLocalPath() ?? string.Empty; - _native.SelectFolderDialog((IAvnWindow)_window.Native, events, options.AllowMultiple.AsComBool(), options.Title ?? "", suggestedDirectory); + _native.SelectFolderDialog((IAvnWindow)_topLevel.Native, events, options.AllowMultiple.AsComBool(), options.Title ?? "", suggestedDirectory); var result = await events.Task.ConfigureAwait(false); return result?.Select(f => new BclStorageFolder(new DirectoryInfo(f))).ToArray() diff --git a/src/Avalonia.Native/TopLevelImpl.cs b/src/Avalonia.Native/TopLevelImpl.cs new file mode 100644 index 00000000000..7d19906d294 --- /dev/null +++ b/src/Avalonia.Native/TopLevelImpl.cs @@ -0,0 +1,557 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Avalonia.Automation.Peers; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Input; +using Avalonia.Input.Platform; +using Avalonia.Input.Raw; +using Avalonia.Input.TextInput; +using Avalonia.Native.Interop; +using Avalonia.Platform; +using Avalonia.Platform.Storage; +using Avalonia.Platform.Storage.FileIO; +using Avalonia.Rendering.Composition; +using Avalonia.Threading; + +namespace Avalonia.Native; + +internal class MacOSTopLevelHandle : IPlatformHandle, IMacOSTopLevelPlatformHandle +{ + internal MacOSTopLevelHandle(IAvnTopLevel native) + { + Native = native; + + HandleDescriptor = "NSView"; + + Handle = NSView; + } + + internal MacOSTopLevelHandle(IAvnWindowBase native) + { + Native = native; + + HandleDescriptor = "NSWindow"; + + Handle = NSWindow; + } + + internal IAvnTopLevel Native { get; } + + public IntPtr Handle { get; } + + public string HandleDescriptor { get; } + + public IntPtr NSView => Native.ObtainNSViewHandle(); + + public IntPtr GetNSViewRetained() + { + return Native.ObtainNSViewHandleRetained(); + } + + public IntPtr NSWindow => (Native as IAvnWindowBase)?.ObtainNSWindowHandle() ?? IntPtr.Zero; + + public IntPtr GetNSWindowRetained() + { + return (Native as IAvnWindowBase)?.ObtainNSWindowHandleRetained() ?? IntPtr.Zero; + } +} + +internal class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface +{ + protected IInputRoot? _inputRoot; + private NativeControlHostImpl? _nativeControlHost; + private IStorageProvider? _storageProvider; + private PlatformBehaviorInhibition? _platformBehaviorInhibition; + + private readonly MouseDevice? _mouse; + private readonly IKeyboardDevice? _keyboard; + private readonly ICursorFactory? _cursorFactory; + + protected readonly IAvaloniaNativeFactory Factory; + + private Size _savedLogicalSize; + private double _savedScaling; + private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None; + + protected MacOSTopLevelHandle? _handle; + + private object _syncRoot = new object(); + private IEnumerable? _surfaces; + + public TopLevelImpl(IAvaloniaNativeFactory factory) + { + Factory = factory; + + _keyboard = AvaloniaLocator.Current.GetService(); + _mouse = new MouseDevice(); + _cursorFactory = AvaloniaLocator.Current.GetService(); + } + + internal virtual void Init(MacOSTopLevelHandle handle, IAvnScreens screens) + { + _handle = handle; + _savedLogicalSize = ClientSize; + _savedScaling = RenderScaling; + _nativeControlHost = new NativeControlHostImpl(Native!.CreateNativeControlHost()); + _storageProvider = new SystemDialogs(this, Factory.CreateSystemDialogs()); + _platformBehaviorInhibition = new PlatformBehaviorInhibition(Factory.CreatePlatformBehaviorInhibition()); + _surfaces = new object[] { new GlPlatformSurface(Native), new MetalPlatformSurface(Native), this }; + + Screen = new ScreenImpl(screens); + InputMethod = new AvaloniaNativeTextInputMethod(Native); + } + + public double DesktopScaling => 1; + + public IAvnTopLevel? Native => _handle?.Native; + public IPlatformHandle? Handle => _handle; + public AvaloniaNativeTextInputMethod? InputMethod { get; private set; } + public Size ClientSize + { + get + { + if (Native == null) + { + return default; + } + + var s = Native.ClientSize; + return new Size(s.Width, s.Height); + + } + } + public double RenderScaling => Native?.Scaling ?? 1; + public IEnumerable Surfaces => _surfaces ?? Array.Empty(); + public Action? Input { get; set; } + public Action? Paint { get; set; } + public Action? Resized { get; set; } + public Action? ScalingChanged { get; set; } + public Action? TransparencyLevelChanged { get; set; } + public Compositor Compositor => AvaloniaNativePlatform.Compositor; + public Action? Closed { get; set; } + public Action? LostFocus { get; set; } + + public WindowTransparencyLevel TransparencyLevel + { + get => _transparencyLevel; + private set + { + if (_transparencyLevel != value) + { + _transparencyLevel = value; + TransparencyLevelChanged?.Invoke(value); + } + } + } + + public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0); + public virtual void SetFrameThemeVariant(PlatformThemeVariant themeVariant) + { + //noop + } + + public IMouseDevice? MouseDevice => _mouse; + + public INativeControlHostImpl? NativeControlHost => _nativeControlHost; + + public IScreenImpl? Screen { get; private set; } + + public AutomationPeer? GetAutomationPeer() + { + return _inputRoot is Control c ? ControlAutomationPeer.CreatePeerForElement(c) : null; + } + + public bool RawTextInputEvent(ulong timeStamp, string text) + { + if (_inputRoot is null) + return false; + + if (_keyboard is null) + { + return false; + } + + Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); + + var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text); + + Input?.Invoke(args); + + return args.Handled; + } + + public bool RawKeyEvent( + AvnRawKeyEventType type, + ulong timeStamp, + AvnInputModifiers modifiers, + AvnKey key, + AvnPhysicalKey physicalKey, + string keySymbol) + { + if (_inputRoot is null) + return false; + + if (_keyboard is null) + { + return false; + } + + Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); + + var args = new RawKeyEventArgs( + _keyboard, + timeStamp, + _inputRoot, + (RawKeyEventType)type, + (Key)key, + (RawInputModifiers)modifiers, + (PhysicalKey)physicalKey, + keySymbol); + + Input?.Invoke(args); + + return args.Handled; + } + + public void RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) + { + if (_inputRoot is null) + return; + + if (_mouse is null) + { + return; + } + + Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); + + switch (type) + { + case AvnRawMouseEventType.Wheel: + Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, + point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); + break; + + case AvnRawMouseEventType.Magnify: + Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Magnify, + point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); + break; + + case AvnRawMouseEventType.Rotate: + Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Rotate, + point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); + break; + + case AvnRawMouseEventType.Swipe: + Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Swipe, + point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); + break; + + default: + var e = new RawPointerEventArgs(_mouse, timeStamp, _inputRoot, (RawPointerEventType)type, + point.ToAvaloniaPoint(), (RawInputModifiers)modifiers); + + if (!ChromeHitTest(e)) + { + Input?.Invoke(e); + } + break; + } + } + + public void Invalidate() + { + Native?.Invalidate(); + } + + public void SetInputRoot(IInputRoot inputRoot) + { + _inputRoot = inputRoot; + } + + public Point PointToClient(PixelPoint point) + { + return Native?.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint() ?? default; + } + + public PixelPoint PointToScreen(Point point) + { + return Native?.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint() ?? default; + } + + public void SetCursor(ICursorImpl? cursor) + { + if (Native == null) + { + return; + } + + var newCursor = cursor as AvaloniaNativeCursor; + newCursor ??= (_cursorFactory?.GetCursor(StandardCursorType.Arrow) as AvaloniaNativeCursor); + Native.SetCursor(newCursor?.Cursor); + } + + public virtual IPopupImpl CreatePopup() + { + return new PopupImpl(Factory, this); + } + + public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) + { + foreach (var level in transparencyLevels) + { + AvnWindowTransparencyMode? mode = null; + + if (level == WindowTransparencyLevel.None) + mode = AvnWindowTransparencyMode.Opaque; + if (level == WindowTransparencyLevel.Transparent) + mode = AvnWindowTransparencyMode.Transparent; + else if (level == WindowTransparencyLevel.AcrylicBlur) + mode = AvnWindowTransparencyMode.Blur; + + if (mode.HasValue && level != TransparencyLevel) + { + Native?.SetTransparencyMode(mode.Value); + TransparencyLevel = level; + return; + } + } + + // If we get here, we didn't find a supported level. Use the default of None. + if (TransparencyLevel != WindowTransparencyLevel.None) + { + Native?.SetTransparencyMode(AvnWindowTransparencyMode.Opaque); + TransparencyLevel = WindowTransparencyLevel.None; + } + } + + public virtual object? TryGetFeature(Type featureType) + { + if (featureType == typeof(ITextInputMethodImpl)) + { + return InputMethod; + } + + if (featureType == typeof(INativeControlHostImpl)) + { + return _nativeControlHost; + } + + if (featureType == typeof(IStorageProvider)) + { + return _storageProvider; + } + + if (featureType == typeof(IPlatformBehaviorInhibition)) + { + return _platformBehaviorInhibition; + } + + if (featureType == typeof(IClipboard)) + { + return AvaloniaLocator.Current.GetRequiredService(); + } + + if (featureType == typeof(ILauncher)) + { + return new BclLauncher(); + } + + return null; + } + + public virtual void Dispose() + { + Native?.Dispose(); + _handle = null; + + _nativeControlHost?.Dispose(); + _nativeControlHost = null; + + (Screen as ScreenImpl)?.Dispose(); + _mouse?.Dispose(); + } + + protected virtual bool ChromeHitTest(RawPointerEventArgs e) + { + return false; + } + + IFramebufferRenderTarget IFramebufferPlatformSurface.CreateFramebufferRenderTarget() + { + if (!Dispatcher.UIThread.CheckAccess()) + throw new RenderTargetNotReadyException(); + + var nativeRenderTarget = Native?.CreateSoftwareRenderTarget(); + + if (nativeRenderTarget is null) + { + throw new RenderTargetNotReadyException(); + } + + return new FramebufferRenderTarget(this, nativeRenderTarget); + } + + protected internal unsafe class TopLevelEvents : NativeCallbackBase, IAvnTopLevelEvents + { + private readonly TopLevelImpl _parent; + + public TopLevelEvents(TopLevelImpl parent) + { + _parent = parent; + } + + void IAvnTopLevelEvents.Closed() + { + var n = _parent.Native; + + try + { + _parent?.Closed?.Invoke(); + } + finally + { + + _parent?.Dispose(); + n?.Dispose(); + } + } + + void IAvnTopLevelEvents.Paint() + { + Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender); + var s = _parent.ClientSize; + _parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height)); + } + + void IAvnTopLevelEvents.Resized(AvnSize* size, AvnPlatformResizeReason reason) + { + if (_parent?.Native == null) + { + return; + } + + var s = new Size(size->Width, size->Height); + _parent._savedLogicalSize = s; + _parent.Resized?.Invoke(s, (WindowResizeReason)reason); + } + + void IAvnTopLevelEvents.RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) + { + _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta); + } + + int IAvnTopLevelEvents.RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnKey key, AvnPhysicalKey physicalKey, string keySymbol) + { + return _parent.RawKeyEvent(type, timeStamp, modifiers, key, physicalKey, keySymbol).AsComBool(); + } + + int IAvnTopLevelEvents.RawTextInputEvent(ulong timeStamp, string text) + { + return _parent.RawTextInputEvent(timeStamp, text).AsComBool(); + } + + void IAvnTopLevelEvents.ScalingChanged(double scaling) + { + _parent._savedScaling = scaling; + _parent.ScalingChanged?.Invoke(scaling); + } + + void IAvnTopLevelEvents.RunRenderPriorityJobs() + { + Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender); + } + + void IAvnTopLevelEvents.LostFocus() + { + _parent.LostFocus?.Invoke(); + } + + AvnDragDropEffects IAvnTopLevelEvents.DragEvent(AvnDragEventType type, AvnPoint position, + AvnInputModifiers modifiers, + AvnDragDropEffects effects, + IAvnClipboard clipboard, IntPtr dataObjectHandle) + { + var device = AvaloniaLocator.Current.GetService(); + + if (device is null) + { + return AvnDragDropEffects.None; + } + + if (_parent._inputRoot is null) + { + return AvnDragDropEffects.None; + } + + IDataObject? dataObject = null; + if (dataObjectHandle != IntPtr.Zero) + dataObject = GCHandle.FromIntPtr(dataObjectHandle).Target as IDataObject; + + using (var clipboardDataObject = new ClipboardDataObject(clipboard)) + { + if (dataObject == null) + dataObject = clipboardDataObject; + + var args = new RawDragEvent(device, (RawDragEventType)type, + _parent._inputRoot, position.ToAvaloniaPoint(), dataObject, (DragDropEffects)effects, + (RawInputModifiers)modifiers); + _parent.Input(args); + return (AvnDragDropEffects)args.Effects; + } + } + + IAvnAutomationPeer? IAvnTopLevelEvents.AutomationPeer + { + get + { + var native = _parent.GetAutomationPeer(); + + return native is null ? null : AvnAutomationPeer.Wrap(native); + } + } + } + + private class FramebufferRenderTarget : IFramebufferRenderTarget + { + private readonly TopLevelImpl _parent; + private IAvnSoftwareRenderTarget? _target; + + public FramebufferRenderTarget(TopLevelImpl parent, IAvnSoftwareRenderTarget target) + { + _parent = parent; + _target = target; + } + + public void Dispose() + { + lock (_parent._syncRoot) + { + _target?.Dispose(); + _target = null; + } + } + + public ILockedFramebuffer Lock() + { + var w = _parent._savedLogicalSize.Width * _parent._savedScaling; + var h = _parent._savedLogicalSize.Height * _parent._savedScaling; + var dpi = _parent._savedScaling * 96; + return new DeferredFramebuffer(_target, cb => + { + lock (_parent._syncRoot) + { + if (_parent.Native != null && _target != null) + { + cb(_parent.Native); + } + } + }, (int)w, (int)h, new Vector(dpi, dpi)); + } + } +} diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 10a0afc6daa..b18f2e196b6 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -16,7 +16,6 @@ internal class WindowImpl : WindowBaseImpl, IWindowImpl private double _extendTitleBarHeight = -1; private DoubleClickHelper _doubleClickHelper; private readonly ITopLevelNativeMenuExporter _nativeMenuExporter; - private readonly AvaloniaNativeTextInputMethod _inputMethod; private bool _canResize = true; internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(factory) @@ -26,12 +25,15 @@ internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOption using (var e = new WindowEvents(this)) { - Init(_native = factory.CreateWindow(e), factory.CreateScreens()); + Init(new MacOSTopLevelHandle(_native = factory.CreateWindow(e)), factory.CreateScreens()); } _nativeMenuExporter = new AvaloniaNativeMenuExporter(_native, factory); - - _inputMethod = new AvaloniaNativeTextInputMethod(_native); + } + + internal sealed override void Init(MacOSTopLevelHandle handle, IAvnScreens screens) + { + base.Init(handle, screens); } class WindowEvents : WindowBaseEvents, IAvnWindowEvents @@ -65,7 +67,7 @@ void IAvnWindowEvents.GotInputWhenDisabled() _parent.GotInputWhenDisabled?.Invoke(); } } - + public new IAvnWindow Native => _native; public void CanResize(bool value) @@ -211,7 +213,7 @@ public void SetIcon(IWindowIconImpl icon) public void Move(PixelPoint point) => Position = point; public override IPopupImpl CreatePopup() => - _opts.OverlayPopups ? null : new PopupImpl(_factory, this); + _opts.OverlayPopups ? null : new PopupImpl(Factory, this); public Action GotInputWhenDisabled { get; set; } @@ -229,7 +231,7 @@ public override object TryGetFeature(Type featureType) { if(featureType == typeof(ITextInputMethodImpl)) { - return _inputMethod; + return InputMethod; } if (featureType == typeof(ITopLevelNativeMenuExporter)) diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 31ab7c0a70e..1271c842959 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -1,123 +1,44 @@ +#nullable enable + using System; -using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; -using Avalonia.Automation.Peers; using Avalonia.Controls; -using Avalonia.Controls.Platform; -using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; -using Avalonia.Input.Platform; -using Avalonia.Input.Raw; using Avalonia.Native.Interop; using Avalonia.Platform; -using Avalonia.Platform.Storage; -using Avalonia.Platform.Storage.FileIO; -using Avalonia.Rendering.Composition; -using Avalonia.Threading; namespace Avalonia.Native { - internal class MacOSTopLevelWindowHandle : IPlatformHandle, IMacOSTopLevelPlatformHandle + internal abstract class WindowBaseImpl : TopLevelImpl, IWindowBaseImpl { - IAvnWindowBase _native; - - public MacOSTopLevelWindowHandle(IAvnWindowBase native) - { - _native = native; - } - - public IntPtr Handle => NSWindow; - - public string HandleDescriptor => "NSWindow"; - - public IntPtr NSView => _native?.ObtainNSViewHandle() ?? IntPtr.Zero; - - public IntPtr NSWindow => _native?.ObtainNSWindowHandle() ?? IntPtr.Zero; - - public IntPtr GetNSViewRetained() + internal WindowBaseImpl(IAvaloniaNativeFactory factory) : base(factory) { - return _native?.ObtainNSViewHandleRetained() ?? IntPtr.Zero; - } - public IntPtr GetNSWindowRetained() - { - return _native?.ObtainNSWindowHandleRetained() ?? IntPtr.Zero; } - } - internal abstract class WindowBaseImpl : IWindowBaseImpl, - IFramebufferPlatformSurface - { - protected readonly IAvaloniaNativeFactory _factory; - protected IInputRoot _inputRoot; - IAvnWindowBase _native; - private object _syncRoot = new object(); - private readonly MouseDevice _mouse; - private readonly IKeyboardDevice _keyboard; - private readonly ICursorFactory _cursorFactory; - private Size _savedLogicalSize; - private double _savedScaling; - private NativeControlHostImpl _nativeControlHost; - private IStorageProvider _storageProvider; - private PlatformBehaviorInhibition _platformBehaviorInhibition; - private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None; + public new IAvnWindowBase? Native => _handle?.Native as IAvnWindowBase; - internal WindowBaseImpl(IAvaloniaNativeFactory factory) - { - _factory = factory; - - _keyboard = AvaloniaLocator.Current.GetService(); - _mouse = new MouseDevice(); - _cursorFactory = AvaloniaLocator.Current.GetService(); - } - - protected void Init(IAvnWindowBase window, IAvnScreens screens) + public PixelPoint Position { - _native = window; - - Surfaces = new object[] { new GlPlatformSurface(window), new MetalPlatformSurface(window), this }; - Handle = new MacOSTopLevelWindowHandle(window); - Screen = new ScreenImpl(screens); - - _savedLogicalSize = ClientSize; - _savedScaling = RenderScaling; - _nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost()); - _storageProvider = new SystemDialogs(this, _factory.CreateSystemDialogs()); - _platformBehaviorInhibition = new PlatformBehaviorInhibition(_factory.CreatePlatformBehaviorInhibition()); - - var monitor = Screen.AllScreens.OrderBy(x => x.Scaling) - .FirstOrDefault(m => m.Bounds.Contains(Position)); - - Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), WindowResizeReason.Layout); + get => Native?.Position.ToAvaloniaPixelPoint() ?? default; + set => Native?.SetPosition(value.ToAvnPoint()); } - public IAvnWindowBase Native => _native; + public Action? Deactivated { get; set; } + public Action? Activated { get; set; } - public Size ClientSize - { - get - { - if (_native != null) - { - var s = _native.ClientSize; - return new Size(s.Width, s.Height); - } - - return default; - } - } + public Action? PositionChanged { get; set; } public Size? FrameSize { get { - if (_native != null) + if (Native != null) { unsafe { var s = new AvnSize { Width = -1, Height = -1 }; - _native.GetFrameSize(&s); + Native.GetFrameSize(&s); return s.Width < 0 && s.Height < 0 ? null : new Size(s.Width, s.Height); } } @@ -125,471 +46,98 @@ public Size? FrameSize return default; } } - - public IEnumerable Surfaces { get; private set; } - - public INativeControlHostImpl NativeControlHost => _nativeControlHost; - - - IFramebufferRenderTarget IFramebufferPlatformSurface.CreateFramebufferRenderTarget() - { - if (!Dispatcher.UIThread.CheckAccess()) - throw new RenderTargetNotReadyException(); - return new FramebufferRenderTarget(this, _native.CreateSoftwareRenderTarget()); - } - - class FramebufferRenderTarget : IFramebufferRenderTarget - { - private readonly WindowBaseImpl _parent; - private IAvnSoftwareRenderTarget _target; - - public FramebufferRenderTarget(WindowBaseImpl parent, IAvnSoftwareRenderTarget target) - { - _parent = parent; - _target = target; - } - - public void Dispose() - { - lock (_parent._syncRoot) - { - _target?.Dispose(); - _target = null; - } - } - - public ILockedFramebuffer Lock() - { - var w = _parent._savedLogicalSize.Width * _parent._savedScaling; - var h = _parent._savedLogicalSize.Height * _parent._savedScaling; - var dpi = _parent._savedScaling * 96; - return new DeferredFramebuffer(_target, cb => - { - lock (_parent._syncRoot) - { - if (_parent._native != null && _target != null) - { - cb(_parent._native); - } - } - }, (int)w, (int)h, new Vector(dpi, dpi)); - } - } - - public Action LostFocus { get; set; } - public Action Paint { get; set; } - public Action Resized { get; set; } - public Action Closed { get; set; } - public IMouseDevice MouseDevice => _mouse; - public abstract IPopupImpl CreatePopup(); - - public AutomationPeer GetAutomationPeer() - { - return _inputRoot is Control c ? ControlAutomationPeer.CreatePeerForElement(c) : null; - } - - protected unsafe class WindowBaseEvents : NativeCallbackBase, IAvnWindowBaseEvents - { - private readonly WindowBaseImpl _parent; - - public WindowBaseEvents(WindowBaseImpl parent) - { - _parent = parent; - } - - void IAvnWindowBaseEvents.Closed() - { - var n = _parent._native; - try - { - _parent?.Closed?.Invoke(); - } - finally - { - - _parent?.Dispose(); - n?.Dispose(); - } - } - - void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke(); - - void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke(); - - void IAvnWindowBaseEvents.Paint() - { - Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender); - var s = _parent.ClientSize; - _parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height)); - } - - void IAvnWindowBaseEvents.Resized(AvnSize* size, AvnPlatformResizeReason reason) - { - if (_parent?._native != null) - { - var s = new Size(size->Width, size->Height); - _parent._savedLogicalSize = s; - _parent.Resized?.Invoke(s, (WindowResizeReason)reason); - } - } - - void IAvnWindowBaseEvents.PositionChanged(AvnPoint position) - { - _parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint()); - } - - void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) - { - _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta); - } - - int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnKey key, AvnPhysicalKey physicalKey, string keySymbol) - { - return _parent.RawKeyEvent(type, timeStamp, modifiers, key, physicalKey, keySymbol).AsComBool(); - } - - int IAvnWindowBaseEvents.RawTextInputEvent(ulong timeStamp, string text) - { - return _parent.RawTextInputEvent(timeStamp, text).AsComBool(); - } - - void IAvnWindowBaseEvents.ScalingChanged(double scaling) - { - _parent._savedScaling = scaling; - _parent.ScalingChanged?.Invoke(scaling); - } - - void IAvnWindowBaseEvents.RunRenderPriorityJobs() - { - Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender); - } - - void IAvnWindowBaseEvents.LostFocus() - { - _parent.LostFocus?.Invoke(); - } - - public AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position, - AvnInputModifiers modifiers, - AvnDragDropEffects effects, - IAvnClipboard clipboard, IntPtr dataObjectHandle) - { - var device = AvaloniaLocator.Current.GetService(); - - IDataObject dataObject = null; - if (dataObjectHandle != IntPtr.Zero) - dataObject = GCHandle.FromIntPtr(dataObjectHandle).Target as IDataObject; - - using(var clipboardDataObject = new ClipboardDataObject(clipboard)) - { - if (dataObject == null) - dataObject = clipboardDataObject; - - var args = new RawDragEvent(device, (RawDragEventType)type, - _parent._inputRoot, position.ToAvaloniaPoint(), dataObject, (DragDropEffects)effects, - (RawInputModifiers)modifiers); - _parent.Input(args); - return (AvnDragDropEffects)args.Effects; - } - } - - IAvnAutomationPeer IAvnWindowBaseEvents.AutomationPeer - { - get => AvnAutomationPeer.Wrap(_parent.GetAutomationPeer()); - } - } - - public void Activate() + internal override void Init(MacOSTopLevelHandle handle, IAvnScreens screens) { - _native?.Activate(); - } + _handle = handle; - public bool RawTextInputEvent(ulong timeStamp, string text) - { - if (_inputRoot is null) - return false; - - Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); + base.Init(handle, screens); - var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text); + var monitor = Screen!.AllScreens.OrderBy(x => x.Scaling) + .FirstOrDefault(m => m.Bounds.Contains(Position)); - Input?.Invoke(args); - - return args.Handled; - } - - public bool RawKeyEvent( - AvnRawKeyEventType type, - ulong timeStamp, - AvnInputModifiers modifiers, - AvnKey key, - AvnPhysicalKey physicalKey, - string keySymbol) - { - if (_inputRoot is null) - return false; - - Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); - - var args = new RawKeyEventArgs( - _keyboard, - timeStamp, - _inputRoot, - (RawKeyEventType)type, - (Key)key, - (RawInputModifiers)modifiers, - (PhysicalKey)physicalKey, - keySymbol); - - Input?.Invoke(args); - - return args.Handled; + Resize(new Size(monitor!.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), WindowResizeReason.Layout); } - protected virtual bool ChromeHitTest(RawPointerEventArgs e) + public void Activate() { - return false; - } - - public void RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) - { - if (_inputRoot is null) - return; - - Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); - - switch (type) - { - case AvnRawMouseEventType.Wheel: - Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, - point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); - break; - - case AvnRawMouseEventType.Magnify: - Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Magnify, - point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); - break; - - case AvnRawMouseEventType.Rotate: - Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Rotate, - point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); - break; - - case AvnRawMouseEventType.Swipe: - Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Swipe, - point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); - break; - - default: - var e = new RawPointerEventArgs(_mouse, timeStamp, _inputRoot, (RawPointerEventType)type, - point.ToAvaloniaPoint(), (RawInputModifiers)modifiers); - - if(!ChromeHitTest(e)) - { - Input?.Invoke(e); - } - break; - } + Native?.Activate(); } public void Resize(Size clientSize, WindowResizeReason reason) { - _native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason); + Native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason); } - - public Compositor Compositor => AvaloniaNativePlatform.Compositor; - - public virtual void Dispose() - { - _native?.Close(); - _native?.Dispose(); - _native = null; - - _nativeControlHost?.Dispose(); - _nativeControlHost = null; - - (Screen as ScreenImpl)?.Dispose(); - _mouse.Dispose(); - } - - - public void Invalidate(Rect rect) + + public override void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { - _native?.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y }); + Native?.SetFrameThemeVariant((AvnPlatformThemeVariant)themeVariant); } - public void SetInputRoot(IInputRoot inputRoot) + public override void Dispose() { - _inputRoot = inputRoot; + Native?.Close(); + Native?.Dispose(); + _handle = null; } - public virtual void Show(bool activate, bool isDialog) { - _native?.Show(activate.AsComBool(), isDialog.AsComBool()); - } - - - public PixelPoint Position - { - get => _native?.Position.ToAvaloniaPixelPoint() ?? default; - set => _native?.SetPosition(value.ToAvnPoint()); - } - - public Point PointToClient(PixelPoint point) - { - return _native?.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint() ?? default; - } - - public PixelPoint PointToScreen(Point point) - { - return _native?.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint() ?? default; + Native?.Show(activate.AsComBool(), isDialog.AsComBool()); } public void Hide() { - _native?.Hide(); + Native?.Hide(); } public void BeginMoveDrag(PointerPressedEventArgs e) { - _native?.BeginMoveDrag(); + Native?.BeginMoveDrag(); } - public Size MaxAutoSizeHint => Screen.AllScreens.Select(s => s.Bounds.Size.ToSize(1)) + public Size MaxAutoSizeHint => Screen!.AllScreens.Select(s => s.Bounds.Size.ToSize(1)) .OrderByDescending(x => x.Width + x.Height).FirstOrDefault(); public void SetTopmost(bool value) { - _native?.SetTopMost(value.AsComBool()); - } - - public double RenderScaling => _native?.Scaling ?? 1; - - public double DesktopScaling => 1; - - public Action Deactivated { get; set; } - public Action Activated { get; set; } - - public void SetCursor(ICursorImpl cursor) - { - if (_native == null) - { - return; - } - - var newCursor = cursor as AvaloniaNativeCursor; - newCursor = newCursor ?? (_cursorFactory.GetCursor(StandardCursorType.Arrow) as AvaloniaNativeCursor); - _native.SetCursor(newCursor.Cursor); + Native?.SetTopMost(value.AsComBool()); } - public Action PositionChanged { get; set; } - - public Action Input { get; set; } - - public Action ScalingChanged { get; set; } - - public Action TransparencyLevelChanged { get; set; } - - public IScreenImpl Screen { get; private set; } - // TODO - - public void SetMinMaxSize(Size minSize, Size maxSize) - { - _native?.SetMinMaxSize(minSize.ToAvnSize(), maxSize.ToAvnSize()); - } - public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) { } - internal void BeginDraggingSession(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard clipboard, - IAvnDndResultCallback callback, IntPtr sourceHandle) - { - _native?.BeginDragAndDropOperation(effects, point, clipboard, callback, sourceHandle); - } - - public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) - { - foreach (var level in transparencyLevels) - { - AvnWindowTransparencyMode? mode = null; - - if (level == WindowTransparencyLevel.None) - mode = AvnWindowTransparencyMode.Opaque; - if (level == WindowTransparencyLevel.Transparent) - mode = AvnWindowTransparencyMode.Transparent; - else if (level == WindowTransparencyLevel.AcrylicBlur) - mode = AvnWindowTransparencyMode.Blur; - - if (mode.HasValue && level != TransparencyLevel) - { - _native?.SetTransparencyMode(mode.Value); - TransparencyLevel = level; - return; - } - } - - // If we get here, we didn't find a supported level. Use the default of None. - if (TransparencyLevel != WindowTransparencyLevel.None) - { - _native?.SetTransparencyMode(AvnWindowTransparencyMode.Opaque); - TransparencyLevel = WindowTransparencyLevel.None; - } - } - - public WindowTransparencyLevel TransparencyLevel + public void SetMinMaxSize(Size minSize, Size maxSize) { - get => _transparencyLevel; - private set - { - if (_transparencyLevel != value) - { - _transparencyLevel = value; - TransparencyLevelChanged?.Invoke(value); - } - } + Native?.SetMinMaxSize(minSize.ToAvnSize(), maxSize.ToAvnSize()); } - public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) + internal void BeginDraggingSession(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard clipboard, + IAvnDndResultCallback callback, IntPtr sourceHandle) { - _native.SetFrameThemeVariant((AvnPlatformThemeVariant)themeVariant); + Native?.BeginDragAndDropOperation(effects, point, clipboard, callback, sourceHandle); } - public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0); - public virtual object TryGetFeature(Type featureType) + protected class WindowBaseEvents : TopLevelEvents, IAvnWindowBaseEvents { - if (featureType == typeof(INativeControlHostImpl)) - { - return _nativeControlHost; - } - - if (featureType == typeof(IStorageProvider)) - { - return _storageProvider; - } + private readonly WindowBaseImpl _parent; - if (featureType == typeof(IPlatformBehaviorInhibition)) + public WindowBaseEvents(WindowBaseImpl parent) : base(parent) { - return _platformBehaviorInhibition; + _parent = parent; } - if (featureType == typeof(IClipboard)) + void IAvnWindowBaseEvents.PositionChanged(AvnPoint position) { - return AvaloniaLocator.Current.GetRequiredService(); + _parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint()); } - if (featureType == typeof(ILauncher)) - { - return new BclLauncher(); - } + void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke(); - return null; + void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke(); } - - public IPlatformHandle Handle { get; private set; } } } diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 5ea53d22d10..04e42c2bf73 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -663,6 +663,7 @@ interface IAvaloniaNativeFactory : IUnknown HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, IAvnApplicationEvents* appCb, IAvnDispatcher* dispatcher); IAvnMacOptions* GetMacOptions(); + HRESULT CreateTopLevel(IAvnTopLevelEvents* cb, IAvnTopLevel** ppv); HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv); HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv); HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv); @@ -692,41 +693,53 @@ interface IAvnString : IUnknown HRESULT Length(int*ret); } -[uuid(e5aca675-02b7-4129-aa79-d6e417210bda)] -interface IAvnWindowBase : IUnknown +[uuid(e8cccd3e-e6dc-430a-a0b9-2ce7d7922de6)] +interface IAvnTopLevel : IUnknown { + HRESULT GetClientSize(AvnSize*ret); + HRESULT GetScaling(double*ret); + + HRESULT Invalidate(); + + HRESULT PointToClient(AvnPoint point, AvnPoint*ret); + HRESULT PointToScreen(AvnPoint point, AvnPoint*ret); + + HRESULT SetCursor(IAvnCursor* cursor); + HRESULT CreateGlRenderTarget(IAvnGlContext* context, IAvnGlSurfaceRenderTarget** ret); + HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget** ret); + HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget** ret); + + HRESULT ObtainNSViewHandle([intptr]void** retOut); + HRESULT ObtainNSViewHandleRetained([intptr]void** retOut); + + HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut); + + HRESULT GetInputMethod(IAvnTextInputMethod **ppv); + HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode); +} + +[uuid(e5aca675-02b7-4129-aa79-d6e417210bda), cpp-virtual-inherits] +interface IAvnWindowBase : IAvnTopLevel +{ + HRESULT GetFrameSize(AvnSize*result); + HRESULT SetFrameThemeVariant(AvnPlatformThemeVariant mode); + HRESULT SetParent(IAvnWindowBase* parent); HRESULT Show(bool activate, bool isDialog); HRESULT Hide(); HRESULT Close(); - HRESULT Activate(); - HRESULT GetClientSize(AvnSize*ret); - HRESULT GetFrameSize(AvnSize*result); - HRESULT GetScaling(double*ret); + HRESULT Activate(); HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize); HRESULT Resize(double width, double height, AvnPlatformResizeReason reason); - HRESULT Invalidate(AvnRect rect); HRESULT BeginMoveDrag(); HRESULT BeginResizeDrag(AvnWindowEdge edge); HRESULT GetPosition(AvnPoint*ret); HRESULT SetPosition(AvnPoint point); - HRESULT PointToClient(AvnPoint point, AvnPoint*ret); - HRESULT PointToScreen(AvnPoint point, AvnPoint*ret); HRESULT SetTopMost(bool value); - HRESULT SetCursor(IAvnCursor* cursor); - HRESULT CreateGlRenderTarget(IAvnGlContext* context, IAvnGlSurfaceRenderTarget** ret); - HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget** ret); - HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget** ret); HRESULT SetMainMenu(IAvnMenu* menu); HRESULT ObtainNSWindowHandle([intptr]void** retOut); HRESULT ObtainNSWindowHandleRetained([intptr]void** retOut); - HRESULT ObtainNSViewHandle([intptr]void** retOut); - HRESULT ObtainNSViewHandleRetained([intptr]void** retOut); - HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut); HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard* clipboard, IAvnDndResultCallback* cb, [intptr]void* sourceHandle); - HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode); - HRESULT SetFrameThemeVariant(AvnPlatformThemeVariant mode); - HRESULT GetInputMethod(IAvnTextInputMethod **ppv); } [uuid(83e588f3-6981-4e48-9ea0-e1e569f79a91), cpp-virtual-inherits] @@ -739,7 +752,6 @@ interface IAvnPopup : IAvnWindowBase interface IAvnWindow : IAvnWindowBase { HRESULT SetEnabled(bool enable); - HRESULT SetParent(IAvnWindow* parent); HRESULT SetCanResize(bool value); HRESULT SetDecorations(SystemDecorations value); HRESULT SetTitle(char* utf8Title); @@ -754,30 +766,39 @@ interface IAvnWindow : IAvnWindowBase HRESULT GetWindowZOrder(long*ret); } +[uuid(fda9c1b3-69e0-43d7-9459-8cc97cb41f6a)] +interface IAvnTopLevelEvents : IUnknown +{ + void Closed(); + + HRESULT Paint(); + void Resized([const] AvnSize& size, AvnPlatformResizeReason reason); + void RawMouseEvent(AvnRawMouseEventType type, + u_int64_t timeStamp, + AvnInputModifiers modifiers, + AvnPoint point, + AvnVector delta); + bool RawKeyEvent(AvnRawKeyEventType type, u_int64_t timeStamp, AvnInputModifiers modifiers, + AvnKey key, AvnPhysicalKey physicalKey, [const] char* keySymbol); + bool RawTextInputEvent(u_int64_t timeStamp, [const] char* text); + void ScalingChanged(double scaling); + void RunRenderPriorityJobs(); + void LostFocus(); + + IAvnAutomationPeer* GetAutomationPeer(); + + + AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position, + AvnInputModifiers modifiers, AvnDragDropEffects effects, + IAvnClipboard* clipboard, [intptr]void* dataObjectHandle); +} + [uuid(939b6599-40a8-4710-a4c8-5d72d8f174fb)] -interface IAvnWindowBaseEvents : IUnknown +interface IAvnWindowBaseEvents : IAvnTopLevelEvents { - HRESULT Paint(); - void Closed(); void Activated(); void Deactivated(); - void Resized([const] AvnSize& size, AvnPlatformResizeReason reason); void PositionChanged(AvnPoint position); - void RawMouseEvent(AvnRawMouseEventType type, - u_int64_t timeStamp, - AvnInputModifiers modifiers, - AvnPoint point, - AvnVector delta); - bool RawKeyEvent(AvnRawKeyEventType type, u_int64_t timeStamp, AvnInputModifiers modifiers, - AvnKey key, AvnPhysicalKey physicalKey, [const] char* keySymbol); - bool RawTextInputEvent(u_int64_t timeStamp, [const] char* text); - void ScalingChanged(double scaling); - void RunRenderPriorityJobs(); - void LostFocus(); - AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position, - AvnInputModifiers modifiers, AvnDragDropEffects effects, - IAvnClipboard* clipboard, [intptr]void* dataObjectHandle); - IAvnAutomationPeer* GetAutomationPeer(); } [uuid(1ae178ee-1fcc-447f-b6dd-b7bb727f934c)] diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 7a65ef54c76..09a2549b4ef 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -134,6 +134,8 @@ public IWindowImpl CreateWindow() return new X11Window(this, null); } + public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow(); + public IWindowImpl CreateEmbeddableWindow() { throw new NotSupportedException(); diff --git a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs index 9e5742954b2..ed068fe5588 100644 --- a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs +++ b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs @@ -116,6 +116,9 @@ public void SetTransparencyLevelHint(IReadOnlyList tran { } + public double DesktopScaling => RenderScaling; + public IScreenImpl? Screen { get; } + public IPlatformHandle? Handle { get; } public Size ClientSize => _surface?.ClientSize ?? new Size(1, 1); public Size? FrameSize => null; public double RenderScaling => _surface?.Scaling ?? 1; diff --git a/src/Browser/Avalonia.Browser/WindowingPlatform.cs b/src/Browser/Avalonia.Browser/WindowingPlatform.cs index 27e5537431f..0602321c50d 100644 --- a/src/Browser/Avalonia.Browser/WindowingPlatform.cs +++ b/src/Browser/Avalonia.Browser/WindowingPlatform.cs @@ -16,11 +16,17 @@ internal class BrowserWindowingPlatform : IWindowingPlatform public IWindowImpl CreateWindow() => throw new NotSupportedException("Browser doesn't support windowing platform. In order to display a single-view content, set ISingleViewApplicationLifetime.MainView."); + IWindowImpl IWindowingPlatform.CreateEmbeddableWindow() { throw new NotImplementedException("Browser doesn't support embeddable windowing platform."); } + ITopLevelImpl IWindowingPlatform.CreateEmbeddableTopLevel() + { + throw new NotImplementedException(); + } + public ITrayIconImpl? CreateTrayIcon() { return null; diff --git a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs index 9bb587c9b1a..1a4ad3c81f7 100644 --- a/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs +++ b/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs @@ -57,6 +57,7 @@ public HeadlessWindowingPlatform(PixelFormat frameBufferFormat) _frameBufferFormat = frameBufferFormat; } public IWindowImpl CreateWindow() => new HeadlessWindowImpl(false, _frameBufferFormat); + public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow(); public IWindowImpl CreateEmbeddableWindow() => throw new PlatformNotSupportedException(); diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index 5843b5400e1..7ba9d42159b 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -53,6 +53,9 @@ public void SetCursor(ICursorImpl cursor) { } + public double DesktopScaling => 1; + public IScreenImpl Screen { get; } + public IPlatformHandle Handle { get; } public Size ClientSize => ScaledSize; public Size? FrameSize => null; public IMouseDevice MouseDevice => new MouseDevice(); diff --git a/src/Tizen/Avalonia.Tizen/Stubs.cs b/src/Tizen/Avalonia.Tizen/Stubs.cs index f3f6f6f148d..70bc8f49532 100644 --- a/src/Tizen/Avalonia.Tizen/Stubs.cs +++ b/src/Tizen/Avalonia.Tizen/Stubs.cs @@ -9,6 +9,8 @@ internal class WindowingPlatformStub : IWindowingPlatform public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException(); + public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow(); + public ITrayIconImpl? CreateTrayIcon() => null; } diff --git a/src/Tizen/Avalonia.Tizen/TopLevelImpl.cs b/src/Tizen/Avalonia.Tizen/TopLevelImpl.cs index 7253064ab32..6425a0e9fe1 100644 --- a/src/Tizen/Avalonia.Tizen/TopLevelImpl.cs +++ b/src/Tizen/Avalonia.Tizen/TopLevelImpl.cs @@ -26,6 +26,10 @@ public TopLevelImpl(ITizenView view, IEnumerable surfaces) _clipboard = new NuiClipboardImpl(); } + public double DesktopScaling => RenderScaling; + public IScreenImpl? Screen { get; } + public IPlatformHandle? Handle { get; } + public Size ClientSize => _view.ClientSize; public Size? FrameSize => null; diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs index 7334186c804..8824a0a7207 100644 --- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs @@ -54,7 +54,7 @@ public override IRawElementProviderSimple? HostRawElementProvider { get { - var handle = WindowImpl?.Handle.Handle ?? IntPtr.Zero; + var handle = WindowImpl?.Handle?.Handle ?? IntPtr.Zero; if (handle == IntPtr.Zero) return null; var hr = UiaCoreProviderApi.UiaHostProviderFromHwnd(handle, out var result); diff --git a/src/Windows/Avalonia.Win32/PopupImpl.cs b/src/Windows/Avalonia.Win32/PopupImpl.cs index a91ac50c77e..8889ca59b89 100644 --- a/src/Windows/Avalonia.Win32/PopupImpl.cs +++ b/src/Windows/Avalonia.Win32/PopupImpl.cs @@ -39,7 +39,7 @@ public override void Show(bool activate, bool isDialog) var focusOwner = UnmanagedMethods.GetFocus(); if (focusOwner != IntPtr.Zero && UnmanagedMethods.GetAncestor(focusOwner, UnmanagedMethods.GetAncestorFlags.GA_ROOT) - == parent.Handle.Handle) + == parent.Handle?.Handle) UnmanagedMethods.SetFocus(parent.Handle.Handle); } @@ -116,7 +116,7 @@ protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr l // One fabulous design decision leads to another, I guess private static IWindowBaseImpl SaveParentHandle(IWindowBaseImpl parent) { - s_parentHandle = parent.Handle.Handle; + s_parentHandle = parent.Handle!.Handle; return parent; } diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 6154dff307e..4fe78b4b9da 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -78,9 +78,14 @@ public void InvalidateScreensCache() /// public Screen? ScreenFromWindow(IWindowBaseImpl window) { - var handle = window.Handle.Handle; + var handle = window.Handle?.Handle; - var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONULL); + if (handle is null) + { + return null; + } + + var monitor = MonitorFromWindow(handle.Value, MONITOR.MONITOR_DEFAULTTONULL); return FindScreenByHandle(monitor); } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index d923f382754..09b268291b6 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -215,6 +215,8 @@ public IWindowImpl CreateWindow() return new WindowImpl(); } + public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow(); + public IWindowImpl CreateEmbeddableWindow() { var embedded = new EmbeddedWindowImpl(); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 8a09cff07f0..805e6c0d347 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -256,7 +256,7 @@ public Size ClientSize } } - Size? ITopLevelImpl.FrameSize => FrameSize; + Size? IWindowBaseImpl.FrameSize => FrameSize; public Size FrameSize { diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index 43317286c93..cea61648dd6 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs @@ -221,6 +221,9 @@ public void SetTransparencyLevelHint(IReadOnlyList tran // No-op } + public double DesktopScaling => RenderScaling; + public IScreenImpl? Screen { get; } + public IPlatformHandle? Handle { get; } public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height); public Size? FrameSize => null; public double RenderScaling => _view.ContentScaleFactor; diff --git a/src/iOS/Avalonia.iOS/Stubs.cs b/src/iOS/Avalonia.iOS/Stubs.cs index a5dbff7797e..17202d4c9de 100644 --- a/src/iOS/Avalonia.iOS/Stubs.cs +++ b/src/iOS/Avalonia.iOS/Stubs.cs @@ -20,6 +20,7 @@ public void Dispose() { } internal class WindowingPlatformStub : IWindowingPlatform { public IWindowImpl CreateWindow() => throw new NotSupportedException(); + public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow(); public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException(); diff --git a/tests/Avalonia.UnitTests/CompositorTestServices.cs b/tests/Avalonia.UnitTests/CompositorTestServices.cs index 9201cbbd5d0..369b39374e9 100644 --- a/tests/Avalonia.UnitTests/CompositorTestServices.cs +++ b/tests/Avalonia.UnitTests/CompositorTestServices.cs @@ -158,6 +158,9 @@ public void Dispose() } + public double DesktopScaling => 1; + public IScreenImpl Screen { get; } + public IPlatformHandle Handle { get; } public Size ClientSize { get; } public Size? FrameSize { get; } public double RenderScaling => 1; diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index abd200ec7c1..8fb74187420 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -152,6 +152,8 @@ public IWindowImpl CreateWindow() } } + public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow(); + public IWindowImpl CreateEmbeddableWindow() { throw new NotImplementedException();