From b3af7f0c6a012512520a09ac06a39fa5fc1b24c0 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Wed, 4 May 2022 14:45:02 +0200 Subject: [PATCH 01/70] Proper Canvas positioning for adorner layer --- src/Avalonia.Controls/Canvas.cs | 75 +++++++++++-------- .../Primitives/AdornerLayer.cs | 2 +- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/Avalonia.Controls/Canvas.cs b/src/Avalonia.Controls/Canvas.cs index fabf8978c7..adee7d4d90 100644 --- a/src/Avalonia.Controls/Canvas.cs +++ b/src/Avalonia.Controls/Canvas.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using Avalonia.Input; using Avalonia.Layout; @@ -159,47 +160,57 @@ namespace Avalonia.Controls } /// - /// Arranges the control's children. + /// Arranges a single child. /// - /// The size allocated to the control. - /// The space taken. - protected override Size ArrangeOverride(Size finalSize) + /// The child to arrange. + /// The size allocated to the canvas. + protected virtual void ArrangeChild(Control child, Size finalSize) { - foreach (Control child in Children) - { - double x = 0.0; - double y = 0.0; - double elementLeft = GetLeft(child); + double x = 0.0; + double y = 0.0; + double elementLeft = GetLeft(child); - if (!double.IsNaN(elementLeft)) - { - x = elementLeft; - } - else + if (!double.IsNaN(elementLeft)) + { + x = elementLeft; + } + else + { + // Arrange with right. + double elementRight = GetRight(child); + if (!double.IsNaN(elementRight)) { - // Arrange with right. - double elementRight = GetRight(child); - if (!double.IsNaN(elementRight)) - { - x = finalSize.Width - child.DesiredSize.Width - elementRight; - } + x = finalSize.Width - child.DesiredSize.Width - elementRight; } + } - double elementTop = GetTop(child); - if (!double.IsNaN(elementTop) ) - { - y = elementTop; - } - else + double elementTop = GetTop(child); + if (!double.IsNaN(elementTop)) + { + y = elementTop; + } + else + { + double elementBottom = GetBottom(child); + if (!double.IsNaN(elementBottom)) { - double elementBottom = GetBottom(child); - if (!double.IsNaN(elementBottom)) - { - y = finalSize.Height - child.DesiredSize.Height - elementBottom; - } + y = finalSize.Height - child.DesiredSize.Height - elementBottom; } + } - child.Arrange(new Rect(new Point(x, y), child.DesiredSize)); + child.Arrange(new Rect(new Point(x, y), child.DesiredSize)); + } + + /// + /// Arranges the control's children. + /// + /// The size allocated to the control. + /// The space taken. + protected override Size ArrangeOverride(Size finalSize) + { + foreach (Control child in Children) + { + ArrangeChild(child, finalSize); } return finalSize; diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index fb047d93df..5ad4e39baf 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -105,7 +105,7 @@ namespace Avalonia.Controls.Primitives } else { - child.Arrange(new Rect(finalSize)); + ArrangeChild((Control) child, finalSize); } } } From 66d7ffe2e99d8809bf1801bd7df20397db4260e0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:23:52 +0100 Subject: [PATCH 02/70] use content min/max size for minsize. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fb67addce1..43a27a34b2 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -225,8 +225,8 @@ HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { START_COM_CALL; @autoreleasepool { - [Window setMinSize:ToNSSize(minSize)]; - [Window setMaxSize:ToNSSize(maxSize)]; + [Window setContentMinSize:ToNSSize(minSize)]; + [Window setContentMaxSize:ToNSSize(maxSize)]; return S_OK; } @@ -243,8 +243,8 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso auto resizeBlock = ResizeScope(View, reason); @autoreleasepool { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; + auto maxSize = [Window contentMaxSize]; + auto minSize = [Window contentMinSize]; if (x < minSize.width) { x = minSize.width; From 4cba4519f3e76d368384a40a9e655a76eedcf7f9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:45:47 +0100 Subject: [PATCH 03/70] only create the NSWindow when show is called. Cache sizes until needed. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 11 ++-- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 53 ++++++++++++++----- native/Avalonia.Native/src/OSX/window.h | 2 +- native/Avalonia.Native/src/OSX/window.mm | 6 ++- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index d2690cee41..19c443806c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -24,10 +24,7 @@ BEGIN_INTERFACE_MAP() INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) END_INTERFACE_MAP() - virtual ~WindowBaseImpl() { - View = NULL; - Window = NULL; - } + virtual ~WindowBaseImpl(); AutoFitContentView *StandardContainer; AvnView *View; @@ -36,6 +33,9 @@ BEGIN_INTERFACE_MAP() ComPtr _glContext; NSObject *renderTarget; AvnPoint lastPositionSet; + NSSize lastSize; + NSSize lastMinSize; + NSSize lastMaxSize; NSString *_lastTitle; bool _shown; @@ -116,6 +116,9 @@ protected: void UpdateStyle(); +private: + void InitialiseNSWindow (); + }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fb67addce1..fe3a209d3e 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -12,6 +12,13 @@ #import "cursor.h" #include "ResizeScope.h" #import "AutoFitContentView.h" +#include "WindowBaseImpl.h" + + +WindowBaseImpl::~WindowBaseImpl() { + View = nullptr; + Window = nullptr; +} WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; @@ -22,17 +29,11 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) View = [[AvnView alloc] initWithParent:this]; StandardContainer = [[AutoFitContentView new] initWithContent:View]; - Window = [[AvnWindow alloc] initWithParent:this]; - [Window setContentView:StandardContainer]; - lastPositionSet.X = 100; lastPositionSet.Y = 100; _lastTitle = @""; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setOpaque:false]; + Window = nullptr; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -83,6 +84,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { + InitialiseNSWindow(); + SetPosition(lastPositionSet); UpdateStyle(); @@ -97,6 +100,10 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window orderFront:Window]; } + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + [Window setContentSize: lastSize]; + _shown = true; return S_OK; @@ -225,8 +232,13 @@ HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { START_COM_CALL; @autoreleasepool { - [Window setMinSize:ToNSSize(minSize)]; - [Window setMaxSize:ToNSSize(maxSize)]; + lastMinSize = ToNSSize(minSize); + lastMaxSize = ToNSSize(maxSize); + + if(Window != nullptr) { + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + } return S_OK; } @@ -243,8 +255,8 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso auto resizeBlock = ResizeScope(View, reason); @autoreleasepool { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; + auto maxSize = lastMaxSize; + auto minSize = lastMinSize; if (x < minSize.width) { x = minSize.width; @@ -267,8 +279,12 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso BaseEvents->Resized(AvnSize{x, y}, reason); } - [Window setContentSize:NSSize{x, y}]; - [Window invalidateShadow]; + lastSize = NSSize {x, y}; + + if(Window != nullptr) { + [Window setContentSize:lastSize]; + [Window invalidateShadow]; + } } @finally { _inResize = false; @@ -502,4 +518,13 @@ NSWindowStyleMask WindowBaseImpl::GetStyle() { void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; -} \ No newline at end of file +} + +void WindowBaseImpl::InitialiseNSWindow() { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{ 0, 0, lastSize } styleMask:GetStyle()]; + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setOpaque:false]; +} diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index e9b98ce9ae..a8c31e7b60 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -8,7 +8,7 @@ class WindowBaseImpl; @interface AvnWindow : NSWindow --(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; +-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; -(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) restoreParentWindow; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 68a459d088..965b4a849b 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -143,9 +143,10 @@ _canBecomeKeyAndMain = true; } --(AvnWindow*) initWithParent: (WindowBaseImpl*) parent +-(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { - self = [super init]; + self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; + [self setReleasedWhenClosed:false]; _parent = parent; [self setDelegate:self]; @@ -155,6 +156,7 @@ [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; + _isExtended = false; return self; } From 0ee1a7e3910525a069230d713c93ed47d3528b4d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:50:42 +0100 Subject: [PATCH 04/70] add explanation for init with content size. --- native/Avalonia.Native/src/OSX/window.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 965b4a849b..68f8f76d75 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -145,6 +145,9 @@ -(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { + // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ + // create nswindow with specific contentRect, otherwise we wont be able to resize the window + // until several ms after the window is physically on the screen. self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; [self setReleasedWhenClosed:false]; From 2372155995ec6680c5ec27570da95a23b9e09cc4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 13:05:40 +0100 Subject: [PATCH 05/70] only create the window if reference is null --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fe3a209d3e..0c85dcc303 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -521,10 +521,12 @@ void WindowBaseImpl::UpdateStyle() { } void WindowBaseImpl::InitialiseNSWindow() { - Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{ 0, 0, lastSize } styleMask:GetStyle()]; - [Window setContentView:StandardContainer]; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; + if(Window == nullptr) { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; - [Window setOpaque:false]; + [Window setOpaque:false]; + } } From e76887fe25830404a0d6afd97782e0f8629c22b0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:14:08 +0100 Subject: [PATCH 06/70] INSWindowHolder uses NSView and NSWindow types. --- native/Avalonia.Native/src/OSX/INSWindowHolder.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h index adc0dd1990..ae64a53e7d 100644 --- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -10,8 +10,8 @@ struct INSWindowHolder { - virtual AvnWindow* _Nonnull GetNSWindow () = 0; - virtual AvnView* _Nonnull GetNSView () = 0; + virtual NSWindow* _Nonnull GetNSWindow () = 0; + virtual NSView* _Nonnull GetNSView () = 0; }; #endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 19c443806c..fd079b1427 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -51,9 +51,9 @@ BEGIN_INTERFACE_MAP() virtual HRESULT ObtainNSViewHandleRetained(void **ret) override; - virtual AvnWindow *GetNSWindow() override; + virtual NSWindow *GetNSWindow() override; - virtual AvnView *GetNSView() override; + virtual NSView *GetNSView() override; virtual HRESULT Show(bool activate, bool isDialog) override; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 0c85dcc303..6ec026ed10 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -60,11 +60,11 @@ HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) { return S_OK; } -AvnWindow *WindowBaseImpl::GetNSWindow() { +NSWindow *WindowBaseImpl::GetNSWindow() { return Window; } -AvnView *WindowBaseImpl::GetNSView() { +NSView *WindowBaseImpl::GetNSView() { return View; } From 0ac5a1c808f192b17afcbb9acc8a9d387432fab8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:16:11 +0100 Subject: [PATCH 07/70] set min and max when creating window. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 6ec026ed10..826486ae31 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -527,6 +527,10 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; + [Window setContentSize: lastSize]; + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + [Window setOpaque:false]; } } From 8be332ab1d1504ff12950821ce8136e1a8647603 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:20:49 +0100 Subject: [PATCH 08/70] make AvnWindowProtocol --- .../project.pbxproj | 12 ++++++++++ .../Avalonia.Native/src/OSX/AvnPanelWindow.h | 9 ++++++++ .../Avalonia.Native/src/OSX/AvnPanelWindow.mm | 8 +++++++ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 6 +++-- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 18 +++++++++------ native/Avalonia.Native/src/OSX/WindowImpl.mm | 8 +++---- .../Avalonia.Native/src/OSX/WindowProtocol.h | 23 +++++++++++++++++++ native/Avalonia.Native/src/OSX/window.h | 15 ++---------- native/Avalonia.Native/src/OSX/window.mm | 10 ++++---- 9 files changed, 78 insertions(+), 31 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.h create mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowProtocol.h diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 88e4c0c682..8ee2a61741 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -9,9 +9,11 @@ /* Begin PBXBuildFile section */ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */; }; 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; + 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */; }; 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; @@ -19,6 +21,7 @@ 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; }; 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; + 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839122E037567BDD1D09DEB /* WindowProtocol.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; @@ -47,6 +50,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 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 = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; @@ -54,10 +58,12 @@ 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; }; 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; + 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = ""; }; 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; }; + 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnPanelWindow.h; sourceTree = ""; }; 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; @@ -166,6 +172,9 @@ 18391D1669284AD2EC9E866A /* AvnView.h */, 1839166350F32661F3ABD70F /* AutoFitContentView.mm */, 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */, + 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */, + 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */, + 1839122E037567BDD1D09DEB /* WindowProtocol.h */, ); sourceTree = ""; }; @@ -193,6 +202,8 @@ 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, + 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */, + 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -277,6 +288,7 @@ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */, + 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h new file mode 100644 index 0000000000..0fe95d044d --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h @@ -0,0 +1,9 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#define NSWindow NSPanel +#define AvnWindow AvnPanelWindow + +//#include "window.h" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm new file mode 100644 index 0000000000..8b79e28ca1 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm @@ -0,0 +1,8 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#define AvnWindow AvnPanelWindow + +//#include "window.mm" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index fd079b1427..19065097fb 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -7,6 +7,7 @@ #define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H #import "rendertarget.h" +#import "WindowProtocol.h" #include "INSWindowHolder.h" @class AutoFitContentView; @@ -28,7 +29,7 @@ BEGIN_INTERFACE_MAP() AutoFitContentView *StandardContainer; AvnView *View; - AvnWindow *Window; + NSWindow * Window; ComPtr BaseEvents; ComPtr _glContext; NSObject *renderTarget; @@ -116,9 +117,10 @@ protected: void UpdateStyle(); + id GetWindowProtocol (); + private: void InitialiseNSWindow (); - }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 826486ae31..82c634dc4f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -100,10 +100,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window orderFront:Window]; } - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - [Window setContentSize: lastSize]; - _shown = true; return S_OK; @@ -132,7 +128,8 @@ HRESULT WindowBaseImpl::Hide() { @autoreleasepool { if (Window != nullptr) { [Window orderOut:Window]; - [Window restoreParentWindow]; + + [GetWindowProtocol() restoreParentWindow]; } return S_OK; @@ -311,10 +308,10 @@ HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { auto nsmenu = nativeMenu->GetNative(); - [Window applyMenu:nsmenu]; + [GetWindowProtocol() applyMenu:nsmenu]; if ([Window isKeyWindow]) { - [Window showWindowMenuWithAppMenu]; + [GetWindowProtocol() showWindowMenuWithAppMenu]; } return S_OK; @@ -532,5 +529,12 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; + + [Window setContentMinSize: lastMinSize]; + [Window setContentMaxSize: lastMaxSize]; } } + +id WindowBaseImpl::GetWindowProtocol() { + return static_cast>(Window); +} diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 0d325c4490..3de4f5d5a8 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -20,7 +20,7 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastWindowState = Normal; _actualWindowState = Normal; WindowEvents = events; - [Window setCanBecomeKeyAndMain]; + [GetWindowProtocol() setCanBecomeKeyAndMain]; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; @@ -68,7 +68,7 @@ HRESULT WindowImpl::SetEnabled(bool enable) { START_COM_CALL; @autoreleasepool { - [Window setEnabled:enable]; + [GetWindowProtocol() setEnabled:enable]; return S_OK; } } @@ -354,7 +354,7 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) { View.layer.zPosition = 0; } - [Window setIsExtended:enable]; + [GetWindowProtocol() setIsExtended:enable]; HideOrShowTrafficLights(); @@ -383,7 +383,7 @@ HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) { return E_POINTER; } - *ret = [Window getExtendedTitleBarHeight]; + *ret = [GetWindowProtocol() getExtendedTitleBarHeight]; return S_OK; } diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h new file mode 100644 index 0000000000..ac20fe8eb1 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -0,0 +1,23 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import + +@class AvnMenu; + +@protocol AvnWindowProtocol +-(void) setCanBecomeKeyAndMain; +-(void) pollModalSession: (NSModalSession _Nonnull) session; +-(void) restoreParentWindow; +-(bool) shouldTryToHandleEvents; +-(void) setEnabled: (bool) enable; +-(void) showAppMenuOnly; +-(void) showWindowMenuWithAppMenu; +-(void) applyMenu:(AvnMenu* _Nullable)menu; + +-(double) getExtendedTitleBarHeight; +-(void) setIsExtended:(bool)value; +-(bool) isDialog; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index a8c31e7b60..1a001dd6e4 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -2,25 +2,14 @@ #define window_h #import "avalonia-native.h" +#import "WindowProtocol.h" @class AvnMenu; class WindowBaseImpl; -@interface AvnWindow : NSWindow +@interface AvnWindow : NSWindow -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; --(void) setCanBecomeKeyAndMain; --(void) pollModalSession: (NSModalSession _Nonnull) session; --(void) restoreParentWindow; --(bool) shouldTryToHandleEvents; --(void) setEnabled: (bool) enable; --(void) showAppMenuOnly; --(void) showWindowMenuWithAppMenu; --(void) applyMenu:(AvnMenu* _Nullable)menu; - --(double) getExtendedTitleBarHeight; --(void) setIsExtended:(bool)value; --(bool) isDialog; @end #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 68f8f76d75..e4bfedaba4 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,11 +1,11 @@ #import -#include "common.h" +#import "common.h" #import "window.h" -#include "menu.h" -#include "automation.h" +#import "menu.h" +#import "automation.h" #import "WindowBaseImpl.h" -#include "WindowImpl.h" -#include "AvnView.h" +#import "WindowImpl.h" +#import "AvnView.h" @implementation AvnWindow { From cd9be07ced12909309b68a68934ba4371855a3c3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:30:36 +0100 Subject: [PATCH 09/70] ensure menu gets applied. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 36 ++++++++++++++----- native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 - 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 19065097fb..d52518820c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -37,6 +37,7 @@ BEGIN_INTERFACE_MAP() NSSize lastSize; NSSize lastMinSize; NSSize lastMaxSize; + AvnMenu* lastMenu; NSString *_lastTitle; bool _shown; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 82c634dc4f..bd763ce2fd 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -34,6 +34,7 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) _lastTitle = @""; Window = nullptr; + lastMenu = nullptr; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -91,6 +92,10 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window setTitle:_lastTitle]; + if(!isDialog) { + [GetWindowProtocol() setCanBecomeKeyAndMain]; + } + if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; [Window makeKeyAndOrderFront:Window]; @@ -306,12 +311,14 @@ HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { auto nativeMenu = dynamic_cast(menu); - auto nsmenu = nativeMenu->GetNative(); + lastMenu = nativeMenu->GetNative(); - [GetWindowProtocol() applyMenu:nsmenu]; + if(Window != nullptr) { + [GetWindowProtocol() applyMenu:lastMenu]; - if ([Window isKeyWindow]) { - [GetWindowProtocol() showWindowMenuWithAppMenu]; + if ([Window isKeyWindow]) { + [GetWindowProtocol() showWindowMenuWithAppMenu]; + } } return S_OK; @@ -524,17 +531,30 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; - [Window setContentSize: lastSize]; + [Window setContentSize:lastSize]; [Window setContentMinSize:lastMinSize]; [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; - [Window setContentMinSize: lastMinSize]; - [Window setContentMaxSize: lastMaxSize]; + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + + if (lastMenu != nullptr) { + [GetWindowProtocol() applyMenu:lastMenu]; + + if ([Window isKeyWindow]) { + [GetWindowProtocol() showWindowMenuWithAppMenu]; + } + } } } id WindowBaseImpl::GetWindowProtocol() { - return static_cast>(Window); + id instance; + if ([Window conformsToProtocol:@protocol(AvnWindowProtocol)]) { + instance = Window; + } + + return instance; } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 3de4f5d5a8..2a25cc69da 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -20,7 +20,6 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastWindowState = Normal; _actualWindowState = Normal; WindowEvents = events; - [GetWindowProtocol() setCanBecomeKeyAndMain]; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; From f008e403cf1e1ef18fa1d6fba631eb3048a18059 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 16:02:50 +0100 Subject: [PATCH 10/70] make it compile 2 versions is AvnWindow (NSWindow / NSPanel version) fix include mess, and pragma once. --- native/Avalonia.Native/inc/rendertarget.h | 5 + .../src/OSX/AutoFitContentView.h | 4 +- .../src/OSX/AutoFitContentView.mm | 3 +- .../project.pbxproj | 26 ++- .../Avalonia.Native/src/OSX/AvnPanelWindow.h | 9 - .../Avalonia.Native/src/OSX/AvnPanelWindow.mm | 7 +- native/Avalonia.Native/src/OSX/AvnView.h | 10 +- native/Avalonia.Native/src/OSX/AvnView.mm | 3 +- .../src/OSX/{window.mm => AvnWindow.mm} | 189 +++++++----------- native/Avalonia.Native/src/OSX/PopupImpl.h | 9 + native/Avalonia.Native/src/OSX/PopupImpl.mm | 68 +++++++ native/Avalonia.Native/src/OSX/ResizeScope.h | 1 - native/Avalonia.Native/src/OSX/ResizeScope.mm | 2 +- .../Avalonia.Native/src/OSX/SystemDialogs.mm | 1 - .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 5 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 19 +- native/Avalonia.Native/src/OSX/WindowImpl.h | 1 - native/Avalonia.Native/src/OSX/WindowImpl.mm | 6 +- .../src/OSX/WindowInterfaces.h | 17 ++ .../Avalonia.Native/src/OSX/WindowProtocol.h | 2 + native/Avalonia.Native/src/OSX/automation.h | 3 +- native/Avalonia.Native/src/OSX/automation.mm | 7 +- native/Avalonia.Native/src/OSX/main.mm | 1 - native/Avalonia.Native/src/OSX/menu.mm | 1 - native/Avalonia.Native/src/OSX/window.h | 15 -- src/Avalonia.Native/avn.idl | 1 + 26 files changed, 235 insertions(+), 180 deletions(-) delete mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.h rename native/Avalonia.Native/src/OSX/{window.mm => AvnWindow.mm} (79%) create mode 100644 native/Avalonia.Native/src/OSX/PopupImpl.h create mode 100644 native/Avalonia.Native/src/OSX/PopupImpl.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowInterfaces.h delete mode 100644 native/Avalonia.Native/src/OSX/window.h diff --git a/native/Avalonia.Native/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h index 2b0338d099..a59915777f 100644 --- a/native/Avalonia.Native/inc/rendertarget.h +++ b/native/Avalonia.Native/inc/rendertarget.h @@ -1,3 +1,8 @@ +#pragma once + +#include "com.h" +#include "comimpl.h" +#include "avalonia-native.h" @protocol IRenderTarget -(void) setNewLayer: (CALayer*) layer; diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.h b/native/Avalonia.Native/src/OSX/AutoFitContentView.h index 68c9fb8dc8..7f1f764141 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.h +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.h @@ -3,8 +3,10 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // +#pragma once + #import -#import "avalonia-native.h" +#include "avalonia-native.h" @interface AutoFitContentView : NSView -(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm index 92d6f67a91..4eaa08cbe2 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -4,7 +4,8 @@ // #include "AvnView.h" -#import "AutoFitContentView.h" +#include "AutoFitContentView.h" +#import "WindowInterfaces.h" @implementation AutoFitContentView { 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 8ee2a61741..6fc3977d4e 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -9,16 +9,19 @@ /* Begin PBXBuildFile section */ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DB45C7D892E61BF388C /* WindowInterfaces.h */; }; 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */; }; 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; - 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */; }; 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; }; + 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839155B28B20FFB672D29C6 /* AvnWindow.mm */; }; + 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183910513F396141938832B5 /* PopupImpl.h */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; + 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391BB698579F40F1783F31 /* PopupImpl.mm */; }; 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; }; 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839122E037567BDD1D09DEB /* WindowProtocol.h */; }; @@ -43,16 +46,17 @@ AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; }; AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; }; - AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; }; AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; }; BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; }; BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 183910513F396141938832B5 /* PopupImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupImpl.h; sourceTree = ""; }; 1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = ""; }; 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; + 1839155B28B20FFB672D29C6 /* AvnWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnWindow.mm; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFitContentView.h; sourceTree = ""; }; 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; }; @@ -60,10 +64,11 @@ 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = ""; }; 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; + 18391BB698579F40F1783F31 /* PopupImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PopupImpl.mm; sourceTree = ""; }; 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; }; - 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnPanelWindow.h; sourceTree = ""; }; + 18391DB45C7D892E61BF388C /* WindowInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowInterfaces.h; sourceTree = ""; }; 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; @@ -78,7 +83,6 @@ 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; }; 37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; }; 37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = ""; }; - 37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = ""; }; 37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = ""; }; 37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = ""; }; 37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; }; @@ -92,7 +96,6 @@ AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; - AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = ""; }; AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = ""; }; @@ -148,8 +151,6 @@ AB661C212148288600291242 /* common.h */, 379860FE214DA0C000CD0246 /* KeyTransform.h */, 37E2330E21583241000CB7E2 /* KeyTransform.mm */, - AB661C1F2148286E00291242 /* window.mm */, - 37C09D8A21581EF2006A6758 /* window.h */, AB00E4F62147CA920032A60A /* main.mm */, 37155CE3233C00EB0034DCE9 /* menu.h */, 520624B222973F4100C4DCEF /* menu.mm */, @@ -173,8 +174,11 @@ 1839166350F32661F3ABD70F /* AutoFitContentView.mm */, 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */, 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */, - 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */, 1839122E037567BDD1D09DEB /* WindowProtocol.h */, + 1839155B28B20FFB672D29C6 /* AvnWindow.mm */, + 18391DB45C7D892E61BF388C /* WindowInterfaces.h */, + 18391BB698579F40F1783F31 /* PopupImpl.mm */, + 183910513F396141938832B5 /* PopupImpl.h */, ); sourceTree = ""; }; @@ -202,8 +206,9 @@ 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, - 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */, 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */, + 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */, + 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -282,13 +287,14 @@ 1A465D10246AB61600C5858B /* dnd.mm in Sources */, AB00E4F72147CA920032A60A /* main.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, - AB661C202148286E00291242 /* window.mm in Sources */, 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */, 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */, 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */, + 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */, + 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h deleted file mode 100644 index 0fe95d044d..0000000000 --- a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// Created by Dan Walmsley on 06/05/2022. -// Copyright (c) 2022 Avalonia. All rights reserved. -// - -#define NSWindow NSPanel -#define AvnWindow AvnPanelWindow - -//#include "window.h" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm index 8b79e28ca1..2365189010 100644 --- a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm @@ -3,6 +3,9 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // -#define AvnWindow AvnPanelWindow +#pragma once + +#define IS_NSPANEL + +#include "AvnWindow.mm" -//#include "window.mm" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h index c6dd90150f..86a68d34c5 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.h +++ b/native/Avalonia.Native/src/OSX/AvnView.h @@ -2,17 +2,15 @@ // Created by Dan Walmsley on 05/05/2022. // Copyright (c) 2022 Avalonia. All rights reserved. // - +#pragma once #import #import #import -#include "window.h" -#import "comimpl.h" -#import "common.h" -#import "WindowImpl.h" -#import "KeyTransform.h" +#include "common.h" +#include "WindowImpl.h" +#include "KeyTransform.h" @class AvnAccessibilityElement; diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 24cbc25502..e6cf73755b 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -4,8 +4,9 @@ // #import -#import "AvnView.h" +#include "AvnView.h" #include "automation.h" +#import "WindowInterfaces.h" @implementation AvnView { diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm similarity index 79% rename from native/Avalonia.Native/src/OSX/window.mm rename to native/Avalonia.Native/src/OSX/AvnWindow.mm index e4bfedaba4..09534a0a4b 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -1,13 +1,32 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + + #import -#import "common.h" -#import "window.h" -#import "menu.h" -#import "automation.h" +#import "WindowProtocol.h" #import "WindowBaseImpl.h" -#import "WindowImpl.h" -#import "AvnView.h" -@implementation AvnWindow +#ifdef IS_NSPANEL +#define BASE_CLASS NSPanel +#define CLASS_NAME AvnPanel +#else +#define BASE_CLASS NSWindow +#define CLASS_NAME AvnWindow +#endif + +#import +#include "common.h" +#include "menu.h" +#include "automation.h" +#include "WindowBaseImpl.h" +#include "WindowImpl.h" +#include "AvnView.h" +#include "WindowInterfaces.h" +#include "PopupImpl.h" + +@implementation CLASS_NAME { ComPtr _parent; bool _canBecomeKeyAndMain; @@ -66,7 +85,7 @@ - (void)pollModalSession:(nonnull NSModalSession)session { auto response = [NSApp runModalSession:session]; - + if(response == NSModalResponseContinue) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -85,18 +104,18 @@ if(_menu != nullptr) { auto appMenuItem = ::GetAppMenuItem(); - + if(appMenuItem != nullptr) { auto appMenu = [appMenuItem menu]; - + [appMenu removeItem:appMenuItem]; - + [_menu insertItem:appMenuItem atIndex:0]; - + [_menu setHasGlobalMenuItem:true]; } - + [NSApp setMenu:_menu]; } else @@ -108,22 +127,22 @@ -(void) showAppMenuOnly { auto appMenuItem = ::GetAppMenuItem(); - + if(appMenuItem != nullptr) { auto appMenu = ::GetAppMenu(); - + auto nativeAppMenu = dynamic_cast(appMenu); - + [[appMenuItem menu] removeItem:appMenuItem]; - + if(_menu != nullptr) { [_menu setHasGlobalMenuItem:false]; } - + [nativeAppMenu->GetNative() addItem:appMenuItem]; - + [NSApp setMenu:nativeAppMenu->GetNative()]; } } @@ -134,7 +153,7 @@ { menu = [AvnMenu new]; } - + _menu = menu; } @@ -143,19 +162,19 @@ _canBecomeKeyAndMain = true; } --(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +-(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ // create nswindow with specific contentRect, otherwise we wont be able to resize the window // until several ms after the window is physically on the screen. self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; - + [self setReleasedWhenClosed:false]; _parent = parent; [self setDelegate:self]; _closed = false; _isEnabled = true; - + [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; @@ -167,12 +186,12 @@ - (BOOL)windowShouldClose:(NSWindow *)sender { auto window = dynamic_cast(_parent.getRaw()); - + if(window != nullptr) { return !window->WindowEvents->Closing(); } - + return true; } @@ -201,16 +220,17 @@ // If the window has a child window being shown as a dialog then don't allow it to become the key window. for(NSWindow* uch in [self childWindows]) { - auto ch = objc_cast(uch); + // TODO protocol + auto ch = objc_cast(uch); if(ch == nil) continue; if (ch.isDialog) return false; } - + return true; } - + return false; } @@ -232,7 +252,7 @@ -(void)becomeKeyWindow { [self showWindowMenuWithAppMenu]; - + if(_parent != nullptr) { _parent->BaseEvents->Activated(); @@ -243,7 +263,8 @@ -(void) restoreParentWindow; { - auto parent = objc_cast([self parentWindow]); + // TODO protocol + auto parent = objc_cast([self parentWindow]); if(parent != nil) { [parent removeChildWindow:self]; @@ -253,7 +274,7 @@ - (void)windowDidMiniaturize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -263,7 +284,7 @@ - (void)windowDidDeminiaturize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -273,7 +294,7 @@ - (void)windowDidResize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -283,7 +304,7 @@ - (void)windowWillExitFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->StartStateTransition(); @@ -293,22 +314,22 @@ - (void)windowDidExitFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->EndStateTransition(); - + if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized) { NSRect screenRect = [[self screen] visibleFrame]; [self setFrame:screenRect display:YES]; } - + if(parent->WindowState() == Minimized) { [self miniaturize:nullptr]; } - + parent->WindowStateChanged(); } } @@ -316,7 +337,7 @@ - (void)windowWillEnterFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->StartStateTransition(); @@ -326,7 +347,7 @@ - (void)windowDidEnterFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->EndStateTransition(); @@ -343,20 +364,20 @@ { if(_parent) _parent->BaseEvents->Deactivated(); - + [self showAppMenuOnly]; - + [super resignKeyWindow]; } - (void)windowDidMove:(NSNotification *)notification { AvnPoint position; - + if(_parent != nullptr) { auto cparent = dynamic_cast(_parent.getRaw()); - + if(cparent != nullptr) { if(cparent->WindowState() == Maximized) @@ -364,7 +385,7 @@ cparent->SetWindowState(Normal); } } - + _parent->GetPosition(&position); _parent->BaseEvents->PositionChanged(position); } @@ -379,7 +400,7 @@ - (void)sendEvent:(NSEvent *)event { [super sendEvent:event]; - + /// This is to detect non-client clicks. This can only be done on Windows... not popups, hence the dynamic_cast. if(_parent != nullptr && dynamic_cast(_parent.getRaw()) != nullptr) { @@ -390,30 +411,30 @@ AvnView* view = _parent->View; NSPoint windowPoint = [event locationInWindow]; NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil]; - + if (!NSPointInRect(viewPoint, view.bounds)) { auto avnPoint = [AvnView toAvnPoint:windowPoint]; auto point = [self translateLocalPoint:avnPoint]; AvnVector delta = { 0, 0 }; - + _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } } - break; - + break; + case NSEventTypeMouseEntered: { _parent->UpdateCursor(); } - break; - + break; + case NSEventTypeMouseExited: { [[NSCursor arrowCursor] set]; } - break; - + break; + default: break; } @@ -422,63 +443,3 @@ @end -class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup -{ -private: - BEGIN_INTERFACE_MAP() - INHERIT_INTERFACE_MAP(WindowBaseImpl) - INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) - END_INTERFACE_MAP() - virtual ~PopupImpl(){} - ComPtr WindowEvents; - PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) - { - WindowEvents = events; - [Window setLevel:NSPopUpMenuWindowLevel]; - } -protected: - virtual NSWindowStyleMask GetStyle() override - { - return NSWindowStyleMaskBorderless; - } - - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - [Window setContentSize:NSSize{x, y}]; - - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; - } - - return S_OK; - } - } -public: - virtual bool ShouldTakeFocusOnShow() override - { - return false; - } -}; - -extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) -{ - @autoreleasepool - { - IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); - return ptr; - } -} - -extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) -{ - @autoreleasepool - { - IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); - return ptr; - } -} diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.h b/native/Avalonia.Native/src/OSX/PopupImpl.h new file mode 100644 index 0000000000..451019a6a4 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/PopupImpl.h @@ -0,0 +1,9 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_POPUPIMPL_H +#define AVALONIA_NATIVE_OSX_POPUPIMPL_H + +#endif //AVALONIA_NATIVE_OSX_POPUPIMPL_H diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm new file mode 100644 index 0000000000..64a8780158 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -0,0 +1,68 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#include "WindowInterfaces.h" +#include "AvnView.h" +#include "WindowImpl.h" +#include "automation.h" +#include "menu.h" +#include "common.h" +#import "WindowBaseImpl.h" +#import "WindowProtocol.h" +#import +#include "PopupImpl.h" + +class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup +{ +private: + BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) + END_INTERFACE_MAP() + virtual ~PopupImpl(){} + ComPtr WindowEvents; + PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) + { + WindowEvents = events; + [Window setLevel:NSPopUpMenuWindowLevel]; + } +protected: + virtual NSWindowStyleMask GetStyle() override + { + return NSWindowStyleMaskBorderless; + } + + virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override + { + START_COM_CALL; + + @autoreleasepool + { + if (Window != nullptr) + { + [Window setContentSize:NSSize{x, y}]; + + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; + } + + return S_OK; + } + } +public: + virtual bool ShouldTakeFocusOnShow() override + { + return false; + } +}; + + +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) +{ + @autoreleasepool + { + IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); + return ptr; + } +} \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h index 7509f93c01..9a43c158fe 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.h +++ b/native/Avalonia.Native/src/OSX/ResizeScope.h @@ -6,7 +6,6 @@ #ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H #define AVALONIA_NATIVE_OSX_RESIZESCOPE_H -#include "window.h" #include "avalonia-native.h" @class AvnView; diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm index 90e7f5cf15..9f1177af8b 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.mm +++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm @@ -5,7 +5,7 @@ #import #include "ResizeScope.h" -#import "AvnView.h" +#include "AvnView.h" ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { _view = view; diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm index 21ad9cfa7c..535b6c3b66 100644 --- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm +++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm @@ -1,5 +1,4 @@ #include "common.h" -#include "window.h" #include "INSWindowHolder.h" class SystemDialogs : public ComSingleObject diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index d52518820c..a8f549b3c6 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -6,11 +6,12 @@ #ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H #define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H -#import "rendertarget.h" -#import "WindowProtocol.h" +#include "rendertarget.h" #include "INSWindowHolder.h" @class AutoFitContentView; +@class AvnMenu; +@protocol AvnWindowProtocol; class WindowBaseImpl : public virtual ComObject, public virtual IAvnWindowBase, diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bd763ce2fd..a58d0bb8be 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -5,14 +5,14 @@ #import #include "common.h" -#import "window.h" -#import "AvnView.h" +#include "AvnView.h" #include "menu.h" #include "automation.h" -#import "cursor.h" +#include "cursor.h" #include "ResizeScope.h" -#import "AutoFitContentView.h" -#include "WindowBaseImpl.h" +#include "AutoFitContentView.h" +#import "WindowProtocol.h" +#import "WindowInterfaces.h" WindowBaseImpl::~WindowBaseImpl() { @@ -558,3 +558,12 @@ id WindowBaseImpl::GetWindowProtocol() { return instance; } + +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) +{ + @autoreleasepool + { + IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); + return ptr; + } +} diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index b229921baa..a4ee4f447c 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -6,7 +6,6 @@ #ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H #define AVALONIA_NATIVE_OSX_WINDOWIMPL_H - #import "WindowBaseImpl.h" #include "IWindowStateChanged.h" diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 2a25cc69da..9992d64b47 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -4,10 +4,10 @@ // #import -#import "window.h" -#import "AutoFitContentView.h" -#import "AvnView.h" +#include "AutoFitContentView.h" +#include "AvnView.h" #include "automation.h" +#include "WindowProtocol.h" WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { _isClientAreaExtended = false; diff --git a/native/Avalonia.Native/src/OSX/WindowInterfaces.h b/native/Avalonia.Native/src/OSX/WindowInterfaces.h new file mode 100644 index 0000000000..6e6d62e85e --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowInterfaces.h @@ -0,0 +1,17 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import +#include "WindowProtocol.h" +#include "WindowBaseImpl.h" + +@interface AvnWindow : NSWindow +-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +@end + +@interface AvnPanel : NSPanel +-(AvnPanel* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index ac20fe8eb1..d81d2f1ed1 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -3,6 +3,8 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // +#pragma once + #import @class AvnMenu; diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h index 1727d21f27..367df3619d 100644 --- a/native/Avalonia.Native/src/OSX/automation.h +++ b/native/Avalonia.Native/src/OSX/automation.h @@ -1,5 +1,6 @@ -#import +#pragma once +#import NS_ASSUME_NONNULL_BEGIN class IAvnAutomationPeer; diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 7a0b3b8127..d0c8d7a9db 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -1,9 +1,8 @@ #include "common.h" -#import "automation.h" -#import "window.h" +#include "automation.h" #include "AvnString.h" -#import "INSWindowHolder.h" -#import "AvnView.h" +#include "INSWindowHolder.h" +#include "AvnView.h" @interface AvnAccessibilityElement (Events) - (void) raiseChildrenChanged; diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 011f881e94..6ee86b21ae 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -1,7 +1,6 @@ //This file will contain actual IID structures #define COM_GUIDS_MATERIALIZE #include "common.h" -#include "window.h" static NSString* s_appTitle = @"Avalonia"; diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index fd74edd772..b05588a441 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -1,7 +1,6 @@ #include "common.h" #include "menu.h" -#import "window.h" #include "KeyTransform.h" #include #include /* For kVK_ constants, and TIS functions. */ diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h deleted file mode 100644 index 1a001dd6e4..0000000000 --- a/native/Avalonia.Native/src/OSX/window.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef window_h -#define window_h - -#import "avalonia-native.h" -#import "WindowProtocol.h" - -@class AvnMenu; - -class WindowBaseImpl; - -@interface AvnWindow : NSWindow --(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; -@end - -#endif /* window_h */ diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 8092004989..d6ef0f8918 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -2,6 +2,7 @@ @clr-access internal @clr-map bool int @cpp-preamble @@ +#pragma once #include "com.h" #include "stddef.h" @@ From 1cca34f56ede6b1c4b2169b64a74a9832331e536 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 17:30:03 +0100 Subject: [PATCH 11/70] actually create nspanels for dialogs. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 35 ++++++++----------- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 28 ++++++++++----- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- .../Avalonia.Native/src/OSX/WindowProtocol.h | 1 - 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 09534a0a4b..d0b23540f9 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -29,7 +29,6 @@ @implementation CLASS_NAME { ComPtr _parent; - bool _canBecomeKeyAndMain; bool _closed; bool _isEnabled; bool _isExtended; @@ -157,11 +156,6 @@ _menu = menu; } --(void) setCanBecomeKeyAndMain -{ - _canBecomeKeyAndMain = true; -} - -(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ @@ -215,28 +209,27 @@ -(BOOL)canBecomeKeyWindow { - if (_canBecomeKeyAndMain) + // If the window has a child window being shown as a dialog then don't allow it to become the key window. + for(NSWindow* uch in [self childWindows]) { - // If the window has a child window being shown as a dialog then don't allow it to become the key window. - for(NSWindow* uch in [self childWindows]) - { - // TODO protocol - auto ch = objc_cast(uch); - if(ch == nil) - continue; - if (ch.isDialog) - return false; - } - - return true; + // TODO protocol + auto ch = objc_cast(uch); + if(ch == nil) + continue; + if (ch.isDialog) + return false; } - return false; + return true; } -(BOOL)canBecomeMainWindow { - return _canBecomeKeyAndMain; +#ifdef IS_NSPANEL + return false; +#else + return true; +#endif } -(bool)shouldTryToHandleEvents diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index a8f549b3c6..ae1e6a7016 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -122,6 +122,7 @@ protected: id GetWindowProtocol (); private: + void CreateNSWindow (bool isDialog); void InitialiseNSWindow (); }; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index a58d0bb8be..414632770f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -13,6 +13,7 @@ #include "AutoFitContentView.h" #import "WindowProtocol.h" #import "WindowInterfaces.h" +#include "WindowBaseImpl.h" WindowBaseImpl::~WindowBaseImpl() { @@ -31,6 +32,9 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) lastPositionSet.X = 100; lastPositionSet.Y = 100; + lastSize = NSSize { 100, 100 }; + lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; + lastMinSize = NSSize { 0, 0 }; _lastTitle = @""; Window = nullptr; @@ -85,6 +89,7 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { + CreateNSWindow(isDialog); InitialiseNSWindow(); SetPosition(lastPositionSet); @@ -92,10 +97,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window setTitle:_lastTitle]; - if(!isDialog) { - [GetWindowProtocol() setCanBecomeKeyAndMain]; - } - if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; [Window makeKeyAndOrderFront:Window]; @@ -524,9 +525,21 @@ void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; } -void WindowBaseImpl::InitialiseNSWindow() { +void WindowBaseImpl::CreateNSWindow(bool isDialog) { if(Window == nullptr) { - Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + if(isDialog) + { + Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + } + else + { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + } + } +} + +void WindowBaseImpl::InitialiseNSWindow() { + if(Window != nullptr) { [Window setContentView:StandardContainer]; [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; @@ -537,9 +550,6 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setOpaque:false]; - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - if (lastMenu != nullptr) { [GetWindowProtocol() applyMenu:lastMenu]; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 9992d64b47..63a38f0c22 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -507,7 +507,7 @@ bool WindowImpl::IsDialog() { } NSWindowStyleMask WindowImpl::GetStyle() { - unsigned long s = NSWindowStyleMaskBorderless; + unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless; switch (_decorations) { case SystemDecorationsNone: diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index d81d2f1ed1..1c97d89f39 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -10,7 +10,6 @@ @class AvnMenu; @protocol AvnWindowProtocol --(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) restoreParentWindow; -(bool) shouldTryToHandleEvents; From d6a4a6c901fec82f9675c3f70e72a7e01c008fb5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 18:04:57 +0100 Subject: [PATCH 12/70] ensure cast to protocol instead of concrete types. --- native/Avalonia.Native/src/OSX/AutoFitContentView.mm | 5 +++-- native/Avalonia.Native/src/OSX/AvnView.mm | 7 ++++++- native/Avalonia.Native/src/OSX/AvnWindow.mm | 6 ++---- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 8 ++++---- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm index 4eaa08cbe2..314c579b76 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -5,7 +5,8 @@ #include "AvnView.h" #include "AutoFitContentView.h" -#import "WindowInterfaces.h" +#include "WindowInterfaces.h" +#include "WindowProtocol.h" @implementation AutoFitContentView { @@ -84,7 +85,7 @@ _settingSize = true; [super setFrameSize:newSize]; - auto window = objc_cast([self window]); + auto window = static_cast>([self window]); // TODO get actual titlebar size diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index e6cf73755b..02526afbcb 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -195,7 +195,12 @@ - (bool) ignoreUserInput:(bool)trigerInputWhenDisabled { - auto parentWindow = objc_cast([self window]); + if(_parent == nullptr) + { + return TRUE; + } + + auto parentWindow = _parent->GetWindowProtocol(); if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) { diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index d0b23540f9..ef4dcc3df4 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -212,8 +212,7 @@ // If the window has a child window being shown as a dialog then don't allow it to become the key window. for(NSWindow* uch in [self childWindows]) { - // TODO protocol - auto ch = objc_cast(uch); + auto ch = static_cast>(uch); if(ch == nil) continue; if (ch.isDialog) @@ -256,8 +255,7 @@ -(void) restoreParentWindow; { - // TODO protocol - auto parent = objc_cast([self parentWindow]); + auto parent = static_cast>([self parentWindow]); if(parent != nil) { [parent removeChildWindow:self]; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index ae1e6a7016..8c82bba98c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -114,13 +114,13 @@ BEGIN_INTERFACE_MAP() virtual bool IsDialog(); + id GetWindowProtocol (); + protected: virtual NSWindowStyleMask GetStyle(); void UpdateStyle(); - id GetWindowProtocol (); - private: void CreateNSWindow (bool isDialog); void InitialiseNSWindow (); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 414632770f..227f348333 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -561,12 +561,12 @@ void WindowBaseImpl::InitialiseNSWindow() { } id WindowBaseImpl::GetWindowProtocol() { - id instance; - if ([Window conformsToProtocol:@protocol(AvnWindowProtocol)]) { - instance = Window; + if(Window == nullptr) + { + return nullptr; } - return instance; + return static_cast>(Window); } extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) From 99e07e68d592771273aa4b795682603d2507d973 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 18:07:01 +0100 Subject: [PATCH 13/70] remove unnecessary cast. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index ef4dcc3df4..33f7f75314 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -255,7 +255,8 @@ -(void) restoreParentWindow; { - auto parent = static_cast>([self parentWindow]); + auto parent = [self parentWindow]; + if(parent != nil) { [parent removeChildWindow:self]; From 3600639d39482ef60efbb573f4586d8019da745a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 8 May 2022 19:17:08 +0100 Subject: [PATCH 14/70] re-create NSWindow if we call show and need to swap out for a dialog. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 4 ++++ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 21 ++++++++++++++----- .../Avalonia.Native/src/OSX/WindowProtocol.h | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 33f7f75314..6ff19ead68 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -433,5 +433,9 @@ } } +- (void)disconnectParent { + _parent = nullptr; +} + @end diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 8c82bba98c..eff13bcb23 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -123,6 +123,7 @@ protected: private: void CreateNSWindow (bool isDialog); + void CleanNSWindow (); void InitialiseNSWindow (); }; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 227f348333..a7c38bf652 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -525,14 +525,25 @@ void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; } +void WindowBaseImpl::CleanNSWindow() { + if(Window != nullptr) { + [GetWindowProtocol() disconnectParent]; + [Window close]; + Window = nullptr; + } +} + void WindowBaseImpl::CreateNSWindow(bool isDialog) { - if(Window == nullptr) { - if(isDialog) - { + if (isDialog) { + if (![Window isKindOfClass:[AvnPanel class]]) { + CleanNSWindow(); + Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; } - else - { + } else { + if (![Window isKindOfClass:[AvnWindow class]]) { + CleanNSWindow(); + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; } } diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index 1c97d89f39..92194706de 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -20,5 +20,6 @@ -(double) getExtendedTitleBarHeight; -(void) setIsExtended:(bool)value; +-(void) disconnectParent; -(bool) isDialog; @end \ No newline at end of file From c756f812ec887cbdf77fd2d3d420a7164f25ee0f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 9 May 2022 11:19:20 +0100 Subject: [PATCH 15/70] remove shadow invalidation hack. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index a7c38bf652..db5eb54e3f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -286,7 +286,6 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso if(Window != nullptr) { [Window setContentSize:lastSize]; - [Window invalidateShadow]; } } @finally { From 5a027c585ab9f827bea077cc0a23f9f0579f9bce Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 9 May 2022 12:44:07 +0100 Subject: [PATCH 16/70] Merge pull request #8103 from AvaloniaUI/fixes/deterministic-builds Fixes/deterministic builds --- .../CompilerExtensions/XamlIlClrPropertyInfoHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs index 871a2a2045..e76e2cd46e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs @@ -55,7 +55,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions return cached.get; } - var name = lst.Count == 0 ? key : key + "_" + Guid.NewGuid().ToString("N"); + var name = lst.Count == 0 ? key : key + "_" + context.Configuration.IdentifierGenerator.GenerateIdentifierPart(); var field = _builder.DefineField(types.IPropertyInfo, name + "!Field", false, true); From 2f1ffbd81e6d573a6d5dfa6b06647a0d071d3903 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 9 May 2022 23:52:15 +0300 Subject: [PATCH 17/70] Make ThreadSafeObjectPool actually thread safe (#8106) * Make ThreadSafeObjectPool actually thread safe --- src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs index c6845485dc..827a02334a 100644 --- a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs +++ b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs @@ -5,12 +5,11 @@ namespace Avalonia.Threading public class ThreadSafeObjectPool where T : class, new() { private Stack _stack = new Stack(); - private object _lock = new object(); public static ThreadSafeObjectPool Default { get; } = new ThreadSafeObjectPool(); public T Get() { - lock (_lock) + lock (_stack) { if(_stack.Count == 0) return new T(); From 08487446d98311daa2d6d2304fc4647f984ea8b9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 10 May 2022 14:19:35 +0100 Subject: [PATCH 18/70] [OSX] cache IsClientAreaExtendedToDecorations, and apply it when NSPanel / NSWindow is created and Shown. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 56 ++++++++++++------- .../ViewModels/MainWindowViewModel.cs | 2 +- src/Avalonia.Native/WindowImpl.cs | 7 +++ 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 63a38f0c22..7ab2b2b5fc 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -55,8 +55,20 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) { @autoreleasepool { _isDialog = isDialog; + + bool created = Window == nullptr; + WindowBaseImpl::Show(activate, isDialog); + if(created) + { + if(_isClientAreaExtended) + { + [GetWindowProtocol() setIsExtended:true]; + SetExtendClientArea(true); + } + } + HideOrShowTrafficLights(); return SetWindowState(_lastWindowState); @@ -327,37 +339,39 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) { @autoreleasepool { _isClientAreaExtended = enable; - if (enable) { - Window.titleVisibility = NSWindowTitleHidden; + if(Window != nullptr) { + if (enable) { + Window.titleVisibility = NSWindowTitleHidden; - [Window setTitlebarAppearsTransparent:true]; + [Window setTitlebarAppearsTransparent:true]; - auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - if (wantsTitleBar) { - [StandardContainer ShowTitleBar:true]; - } else { - [StandardContainer ShowTitleBar:false]; - } + if (wantsTitleBar) { + [StandardContainer ShowTitleBar:true]; + } else { + [StandardContainer ShowTitleBar:false]; + } - if (_extendClientHints & AvnOSXThickTitleBar) { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } else { + Window.toolbar = nullptr; + } } else { + Window.titleVisibility = NSWindowTitleVisible; Window.toolbar = nullptr; + [Window setTitlebarAppearsTransparent:false]; + View.layer.zPosition = 0; } - } else { - Window.titleVisibility = NSWindowTitleVisible; - Window.toolbar = nullptr; - [Window setTitlebarAppearsTransparent:false]; - View.layer.zPosition = 0; - } - [GetWindowProtocol() setIsExtended:enable]; + [GetWindowProtocol() setIsExtended:enable]; - HideOrShowTrafficLights(); + HideOrShowTrafficLights(); - UpdateStyle(); + UpdateStyle(); + } return S_OK; } diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index 2b0c30f311..c44024d952 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels private WindowState _windowState; private WindowState[] _windowStates; private int _transparencyLevel; - private ExtendClientAreaChromeHints _chromeHints; + private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome; private bool _extendClientAreaEnabled; private bool _systemTitleBarEnabled; private bool _preferSystemChromeEnabled; diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index c082bdb1b8..b5af927ea0 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -107,6 +107,13 @@ namespace Avalonia.Native private bool _isExtended; public bool IsClientAreaExtendedToDecorations => _isExtended; + public override void Show(bool activate, bool isDialog) + { + base.Show(activate, isDialog); + + InvalidateExtendedMargins(); + } + protected override bool ChromeHitTest (RawPointerEventArgs e) { if(_isExtended) From 65ed75970dd54e9c68d37c2bf67440c754018d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:15 +0200 Subject: [PATCH 19/70] Fix TextBox property name registrations --- src/Avalonia.Controls/TextBox.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 0be58e7fcc..45e66828c1 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -55,13 +55,13 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(PasswordChar)); public static readonly StyledProperty SelectionBrushProperty = - AvaloniaProperty.Register(nameof(SelectionBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionBrush)); public static readonly StyledProperty SelectionForegroundBrushProperty = - AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionForegroundBrush)); public static readonly StyledProperty CaretBrushProperty = - AvaloniaProperty.Register(nameof(CaretBrushProperty)); + AvaloniaProperty.Register(nameof(CaretBrush)); public static readonly DirectProperty SelectionStartProperty = AvaloniaProperty.RegisterDirect( From 665b0fa3b6db07ef85463453a5e0a0da2a99d065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:26 +0200 Subject: [PATCH 20/70] Fix TextPresenter property name registrations --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 0785149a73..07061940b5 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -26,13 +26,13 @@ namespace Avalonia.Controls.Presenters AvaloniaProperty.Register(nameof(PasswordChar)); public static readonly StyledProperty SelectionBrushProperty = - AvaloniaProperty.Register(nameof(SelectionBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionBrush)); public static readonly StyledProperty SelectionForegroundBrushProperty = - AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionForegroundBrush)); public static readonly StyledProperty CaretBrushProperty = - AvaloniaProperty.Register(nameof(CaretBrushProperty)); + AvaloniaProperty.Register(nameof(CaretBrush)); public static readonly DirectProperty SelectionStartProperty = TextBox.SelectionStartProperty.AddOwner( From d8e01f0e1a67f72f71a81a271c000042934a6196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:35 +0200 Subject: [PATCH 21/70] Fix DockPanel property name registrations --- src/Avalonia.Controls/DockPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/DockPanel.cs b/src/Avalonia.Controls/DockPanel.cs index 8e23555c2d..3e3ed509b5 100644 --- a/src/Avalonia.Controls/DockPanel.cs +++ b/src/Avalonia.Controls/DockPanel.cs @@ -34,7 +34,7 @@ namespace Avalonia.Controls /// public static readonly StyledProperty LastChildFillProperty = AvaloniaProperty.Register( - nameof(LastChildFillProperty), + nameof(LastChildFill), defaultValue: true); /// From d2475dd53b4830ef5332a9de79f8ac9214bd8c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:28:44 +0200 Subject: [PATCH 22/70] Fix owner of IsTextSearchEnabled property --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index bff6799792..164aab32db 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -94,7 +94,7 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty IsTextSearchEnabledProperty = - AvaloniaProperty.Register(nameof(IsTextSearchEnabled), false); + AvaloniaProperty.Register(nameof(IsTextSearchEnabled), false); /// /// Event that should be raised by items that implement to From 3880f1abf41e4494c84af8fc52492f5adfbb4127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:30:19 +0200 Subject: [PATCH 23/70] Fix PasswordCharProperty owner --- src/Avalonia.Controls/MaskedTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs index 933788f9ea..080326606e 100644 --- a/src/Avalonia.Controls/MaskedTextBox.cs +++ b/src/Avalonia.Controls/MaskedTextBox.cs @@ -32,7 +32,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(Mask), string.Empty); public static new readonly StyledProperty PasswordCharProperty = - AvaloniaProperty.Register(nameof(PasswordChar), '\0'); + AvaloniaProperty.Register(nameof(PasswordChar), '\0'); public static readonly StyledProperty PromptCharProperty = AvaloniaProperty.Register(nameof(PromptChar), '_'); From ee33319be119129cef8ca1fe0a243b4d976ee109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:31:25 +0200 Subject: [PATCH 24/70] Fix TickPlacementProperty property owner --- src/Avalonia.Controls/Slider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 64dfce22d4..b6a6f3d02c 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -76,7 +76,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty TickPlacementProperty = - AvaloniaProperty.Register(nameof(TickPlacement), 0d); + AvaloniaProperty.Register(nameof(TickPlacement), 0d); /// /// Defines the property. From da640adb0cec38e95d2910bf54371ac6f973fa9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:40:00 +0200 Subject: [PATCH 25/70] Fix PaneTemplateProperty property owner --- src/Avalonia.Controls/SplitView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs index ae1605a985..532cb1d329 100644 --- a/src/Avalonia.Controls/SplitView.cs +++ b/src/Avalonia.Controls/SplitView.cs @@ -138,7 +138,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty PaneTemplateProperty = - AvaloniaProperty.Register(nameof(PaneTemplate)); + AvaloniaProperty.Register(nameof(PaneTemplate)); /// /// Defines the property From c6eb4138447fd2e9758a8d893fff6b60150268df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:42:10 +0200 Subject: [PATCH 26/70] Use AddOwner for PlacementRectProperty --- src/Avalonia.Controls/ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index ee2378101a..2b122d4174 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -63,7 +63,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty PlacementRectProperty = - AvaloniaProperty.Register(nameof(PlacementRect)); + Popup.PlacementRectProperty.AddOwner(); /// /// Defines the property. From 05fe5fb4ebfc164723ddbe476049499fae675098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:43:15 +0200 Subject: [PATCH 27/70] Fix owner for WindowManagerAddShadowHintProperty property --- src/Avalonia.Controls/Primitives/Popup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 7a7e41b029..95e5e25c42 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -27,7 +27,7 @@ namespace Avalonia.Controls.Primitives #pragma warning restore CS0612 // Type or member is obsolete { public static readonly StyledProperty WindowManagerAddShadowHintProperty = - AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false); + AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false); /// /// Defines the property. From 2d6bd60f691434e667cbbbdff933dfda9d4c0f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:46:41 +0200 Subject: [PATCH 28/70] Rename --- src/Avalonia.Controls/Primitives/Track.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs index 49f0cda982..85343b8fbb 100644 --- a/src/Avalonia.Controls/Primitives/Track.cs +++ b/src/Avalonia.Controls/Primitives/Track.cs @@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(IsDirectionReversed)); public static readonly StyledProperty IgnoreThumbDragProperty = - AvaloniaProperty.Register(nameof(IsThumbDragHandled)); + AvaloniaProperty.Register(nameof(IgnoreThumbDrag)); private double _minimum; private double _maximum = 100.0; @@ -118,7 +118,7 @@ namespace Avalonia.Controls.Primitives set { SetValue(IsDirectionReversedProperty, value); } } - public bool IsThumbDragHandled + public bool IgnoreThumbDrag { get { return GetValue(IgnoreThumbDragProperty); } set { SetValue(IgnoreThumbDragProperty, value); } From da7eecec0788d086fe6a02138755c770509ffe47 Mon Sep 17 00:00:00 2001 From: Kaktusbot Date: Tue, 10 May 2022 22:38:03 +0400 Subject: [PATCH 29/70] Fix missing NotifyCountChanged in AvaloniaList.AddRange --- src/Avalonia.Base/Collections/AvaloniaList.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index 9972e72dd4..a05c9c5da3 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -394,7 +394,13 @@ namespace Avalonia.Collections } while (en.MoveNext()); if (notificationItems is not null) + { NotifyAdd(notificationItems, index); + } + else + { + NotifyCountChanged(); + } } } } From 5eca6cc83e3cbb9cbef2e5b0c48959eee32e78b7 Mon Sep 17 00:00:00 2001 From: Kaktusbot Date: Tue, 10 May 2022 22:57:55 +0400 Subject: [PATCH 30/70] Add test to AvaloniaList.AddRange to notify Count property changed --- .../Collections/AvaloniaListTests.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs index f5ae4cc1e0..b59f272b0c 100644 --- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs +++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs @@ -146,6 +146,23 @@ namespace Avalonia.Base.UnitTests.Collections Assert.True(raised); } + [Fact] + public void AddRange_IEnumerable_Should_Raise_Count_PropertyChanged() + { + var target = new AvaloniaList(new[] { 1, 2, 3, 4, 5 }); + var raised = false; + + target.PropertyChanged += (s, e) => { + Assert.Equal(e.PropertyName, nameof(target.Count)); + Assert.Equal(target.Count, 7); + raised = true; + }; + + target.AddRange(Enumerable.Range(6, 2)); + + Assert.True(raised); + } + [Fact] public void AddRange_Items_Should_Raise_Correct_CollectionChanged() { From a90c4d4f22d737ee4231bacaf9f1763019822687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:47:24 +0200 Subject: [PATCH 31/70] Use correct property name --- src/Avalonia.Controls/Primitives/Track.cs | 2 +- src/Avalonia.Controls/Slider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs index 85343b8fbb..14ec7a2849 100644 --- a/src/Avalonia.Controls/Primitives/Track.cs +++ b/src/Avalonia.Controls/Primitives/Track.cs @@ -442,7 +442,7 @@ namespace Avalonia.Controls.Primitives private void ThumbDragged(object? sender, VectorEventArgs e) { - if (IsThumbDragHandled) + if (IgnoreThumbDrag) return; Value = MathUtilities.Clamp( diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index b6a6f3d02c..be87705b54 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -197,7 +197,7 @@ namespace Avalonia.Controls if (_track != null) { - _track.IsThumbDragHandled = true; + _track.IgnoreThumbDrag = true; } if (_decreaseButton != null) From 409d673215ba683559d062b406ea928303d4bf5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:48:54 +0200 Subject: [PATCH 32/70] Fix MaximumRowsOrColumnsProperty registration name --- src/Avalonia.Base/Layout/UniformGridLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs index 418cd55e41..47c994a350 100644 --- a/src/Avalonia.Base/Layout/UniformGridLayout.cs +++ b/src/Avalonia.Base/Layout/UniformGridLayout.cs @@ -116,7 +116,7 @@ namespace Avalonia.Layout /// Defines the property. /// public static readonly StyledProperty MaximumRowsOrColumnsProperty = - AvaloniaProperty.Register(nameof(MinItemWidth)); + AvaloniaProperty.Register(nameof(MaximumRowsOrColumns)); /// /// Defines the property. From 1e3c5642a3a014fc255d0e11ef1ca7842a7d9f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:52:58 +0200 Subject: [PATCH 33/70] Fix ConicGradientBrush property registrations --- src/Avalonia.Base/Media/ConicGradientBrush.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Media/ConicGradientBrush.cs b/src/Avalonia.Base/Media/ConicGradientBrush.cs index 7c1266fa17..4b50019ddc 100644 --- a/src/Avalonia.Base/Media/ConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/ConicGradientBrush.cs @@ -11,7 +11,7 @@ namespace Avalonia.Media /// Defines the property. /// public static readonly StyledProperty CenterProperty = - AvaloniaProperty.Register( + AvaloniaProperty.Register( nameof(Center), RelativePoint.Center); @@ -19,7 +19,7 @@ namespace Avalonia.Media /// Defines the property. /// public static readonly StyledProperty AngleProperty = - AvaloniaProperty.Register( + AvaloniaProperty.Register( nameof(Angle), 0); From a5b2eb08d4000d7bb4c8c4d1f21afe51bb2031b9 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 11 May 2022 21:12:25 +0300 Subject: [PATCH 34/70] Added support for IReflectableType in InpcPropertyAccessorPlugin --- .../Plugins/InpcPropertyAccessorPlugin.cs | 19 +- .../Data/BindingTests.cs | 19 ++ .../Data/DynamicReflectableType.cs | 221 ++++++++++++++++++ 3 files changed, 252 insertions(+), 7 deletions(-) create mode 100644 tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index 33cecd10a7..b93bf87fdf 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs @@ -17,7 +17,7 @@ namespace Avalonia.Data.Core.Plugins new Dictionary<(Type, string), PropertyInfo?>(); /// - public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null; + public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null; /// /// Starts monitoring the value of a property on an object. @@ -36,7 +36,7 @@ namespace Avalonia.Data.Core.Plugins if (!reference.TryGetTarget(out var instance) || instance is null) return null; - var p = GetFirstPropertyWithName(instance.GetType(), propertyName); + var p = GetFirstPropertyWithName(instance, propertyName); if (p != null) { @@ -50,8 +50,16 @@ namespace Avalonia.Data.Core.Plugins } } - private PropertyInfo? GetFirstPropertyWithName(Type type, string propertyName) + private const BindingFlags PropertyBindingFlags = + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; + + private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName) { + if (instance is IReflectableType reflectableType) + return reflectableType.GetTypeInfo().GetProperty(propertyName, PropertyBindingFlags); + + var type = instance.GetType(); + var key = (type, propertyName); if (!_propertyLookup.TryGetValue(key, out var propertyInfo)) @@ -66,10 +74,7 @@ namespace Avalonia.Data.Core.Plugins { PropertyInfo? found = null; - const BindingFlags bindingFlags = - BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; - - var properties = type.GetProperties(bindingFlags); + var properties = type.GetProperties(PropertyBindingFlags); foreach (PropertyInfo propertyInfo in properties) { diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs index 055de999e2..11a22f0dec 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs @@ -617,6 +617,25 @@ namespace Avalonia.Markup.UnitTests.Data Assert.Equal(0, source.SubscriberCount); } + + [Fact] + public void Binding_Can_Resolve_Property_From_IReflectableType_Type() + { + var source = new DynamicReflectableType { ["Foo"] = "foo" }; + var target = new TwoWayBindingTest { DataContext = source }; + var binding = new Binding + { + Path = "Foo", + }; + + target.Bind(TwoWayBindingTest.TwoWayProperty, binding); + + Assert.Equal("foo", target.TwoWay); + source["Foo"] = "bar"; + Assert.Equal("bar", target.TwoWay); + target.TwoWay = "baz"; + Assert.Equal("baz", source["Foo"]); + } private class StyledPropertyClass : AvaloniaObject { diff --git a/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs b/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs new file mode 100644 index 0000000000..3c57bd38cf --- /dev/null +++ b/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using Moq; + +namespace Avalonia.Markup.UnitTests.Data; + +class DynamicReflectableType : IReflectableType, INotifyPropertyChanged, IEnumerable> +{ + private Dictionary _dic = new(); + + public TypeInfo GetTypeInfo() + { + return new FakeTypeInfo(); + } + + public void Add(string key, object value) + { + _dic.Add(key, value); + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key)); + } + + public object this[string key] + { + get => _dic[key]; + set + { + _dic[key] = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key)); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + public IEnumerator> GetEnumerator() + { + return _dic.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_dic).GetEnumerator(); + } + + + class FakeTypeInfo : TypeInfo + { + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, + ParameterModifier[] modifiers) + { + var propInfo = new Mock(); + propInfo.SetupGet(x => x.Name).Returns(name); + propInfo.SetupGet(x => x.PropertyType).Returns(typeof(object)); + propInfo.SetupGet(x => x.CanWrite).Returns(true); + propInfo.Setup(x => x.GetValue(It.IsAny(), It.IsAny())) + .Returns((object target, object [] _) => ((DynamicReflectableType)target)._dic.GetValueOrDefault(name)); + propInfo.Setup(x => x.SetValue(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((object target, object value, object [] _) => + { + ((DynamicReflectableType)target)._dic[name] = value; + }); + return propInfo.Object; + } + + #region NotSupported + + + public override object[] GetCustomAttributes(bool inherit) + { + throw new NotSupportedException(); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + throw new NotSupportedException(); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + throw new NotSupportedException(); + } + + public override Module Module { get; } + public override string Namespace { get; } + public override string Name { get; } + protected override TypeAttributes GetAttributeFlagsImpl() + { + throw new NotSupportedException(); + } + + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, + Type[] types, ParameterModifier[] modifiers) + { + throw new NotSupportedException(); + } + + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type GetElementType() + { + throw new NotSupportedException(); + } + + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override EventInfo[] GetEvents(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override FieldInfo GetField(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, + Type[] types, ParameterModifier[] modifiers) + { + throw new NotSupportedException(); + } + + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, + ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + throw new NotSupportedException(); + } + + public override Type UnderlyingSystemType { get; } + + protected override bool IsArrayImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsByRefImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsCOMObjectImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsPointerImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsPrimitiveImpl() + { + throw new NotSupportedException(); + } + + public override Assembly Assembly { get; } + public override string AssemblyQualifiedName { get; } + public override Type BaseType { get; } + public override string FullName { get; } + public override Guid GUID { get; } + + + + protected override bool HasElementTypeImpl() + { + throw new NotSupportedException(); + } + + public override Type GetNestedType(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type[] GetNestedTypes(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type GetInterface(string name, bool ignoreCase) + { + throw new NotSupportedException(); + } + + public override Type[] GetInterfaces() + { + throw new NotSupportedException(); + } + + + #endregion + + } +} \ No newline at end of file From 35db70c8d4905eba47f0fd73e2de0830d5e8e2fe Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 12 May 2022 14:37:23 +0200 Subject: [PATCH 35/70] Don't expose viewbox container as logical child. #7735 introduced an internal container control which hosts the child, but it exposed this child in the logical tree, breaking any styles which relied on the `Viewbox.Child` being the logical child of the `Viewbox`. Fix this by introducing a simple internal `ViewboxContainer` control as the container. --- src/Avalonia.Controls/Viewbox.cs | 43 +++++++++++++++++-- .../ViewboxTests.cs | 20 +++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 33a05f126d..f3ec53ed2d 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -8,7 +8,7 @@ namespace Avalonia.Controls /// public class Viewbox : Control { - private Decorator _containerVisual; + private ViewboxContainer _containerVisual; /// /// Defines the property. @@ -37,9 +37,8 @@ namespace Avalonia.Controls public Viewbox() { - _containerVisual = new Decorator(); + _containerVisual = new ViewboxContainer(); _containerVisual.RenderTransformOrigin = RelativePoint.TopLeft; - LogicalChildren.Add(_containerVisual); VisualChildren.Add(_containerVisual); } @@ -88,7 +87,22 @@ namespace Avalonia.Controls if (change.Property == ChildProperty) { + var (oldChild, newChild) = change.GetOldAndNewValue(); + + if (oldChild is not null) + { + ((ISetLogicalParent)oldChild).SetParent(null); + LogicalChildren.Remove(oldChild); + } + _containerVisual.Child = change.GetNewValue(); + + if (newChild is not null) + { + ((ISetLogicalParent)newChild).SetParent(this); + LogicalChildren.Add(newChild); + } + InvalidateMeasure(); } } @@ -129,5 +143,28 @@ namespace Avalonia.Controls return finalSize; } + + private class ViewboxContainer : Control + { + private IControl? _child; + + public IControl? Child + { + get => _child; + set + { + if (_child != value) + { + if (_child is not null) + VisualChildren.Remove(_child); + + _child = value; + + if (_child is not null) + VisualChildren.Add(_child); + } + } + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs index d33e55341b..39a14a6a7e 100644 --- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Shapes; +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.UnitTests; using Xunit; @@ -170,5 +171,24 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(expectedScale, scaleTransform.ScaleX); Assert.Equal(expectedScale, scaleTransform.ScaleY); } + + [Fact] + public void Child_Should_Be_Logical_Child_Of_Viewbox() + { + var target = new Viewbox(); + + Assert.Empty(target.GetLogicalChildren()); + + var child = new Canvas(); + target.Child = child; + + Assert.Single(target.GetLogicalChildren(), child); + Assert.Same(child.GetLogicalParent(), target); + + target.Child = null; + + Assert.Empty(target.GetLogicalChildren()); + Assert.Null(child.GetLogicalParent()); + } } } From 36fefd871704a48ef6fd553845ad6c03e70ea9ad Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 12 May 2022 22:44:29 +0200 Subject: [PATCH 36/70] Fix copypasta. Co-authored-by: Max Katz --- src/Avalonia.Controls/Viewbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index f3ec53ed2d..55c52d8ed9 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -95,7 +95,7 @@ namespace Avalonia.Controls LogicalChildren.Remove(oldChild); } - _containerVisual.Child = change.GetNewValue(); + _containerVisual.Child = newChild; if (newChild is not null) { From ccce304c69385fc3f5c41a3646084792c0f37f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:13:20 +0200 Subject: [PATCH 37/70] Fix TimePicker property registrations --- src/Avalonia.Controls/DateTimePickers/TimePicker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs index f04c79505e..047667567d 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs @@ -38,13 +38,13 @@ namespace Avalonia.Controls /// Defines the property /// public static readonly StyledProperty HeaderProperty = - AvaloniaProperty.Register(nameof(Header)); + AvaloniaProperty.Register(nameof(Header)); /// /// Defines the property /// public static readonly StyledProperty HeaderTemplateProperty = - AvaloniaProperty.Register(nameof(HeaderTemplate)); + AvaloniaProperty.Register(nameof(HeaderTemplate)); /// /// Defines the property From 2ee94118305364b72aee6865d427f1330314bc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:16:05 +0200 Subject: [PATCH 38/70] Fix SelectingItemsControl WrapSelection property owner --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 164aab32db..a730659330 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -118,7 +118,7 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty WrapSelectionProperty = - AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false); + AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false); private static readonly IList Empty = Array.Empty(); private string _textSearchTerm = string.Empty; From 08cbec6f23848dab0680531a059eb8c92babb290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:28:42 +0200 Subject: [PATCH 39/70] Fix ContentPresenter property field types --- .../Presenters/ContentPresenter.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 996cb29534..12a8dd747d 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -52,67 +52,67 @@ namespace Avalonia.Controls.Presenters /// /// Defines the property. /// - public static readonly AttachedProperty ForegroundProperty = + public static readonly StyledProperty ForegroundProperty = TextElement.ForegroundProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontFamilyProperty = + public static readonly StyledProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontSizeProperty = + public static readonly StyledProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStyleProperty = + public static readonly StyledProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontWeightProperty = + public static readonly StyledProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStretchProperty = + public static readonly StyledProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextAlignmentProperty = + public static readonly StyledProperty TextAlignmentProperty = TextBlock.TextAlignmentProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextWrappingProperty = + public static readonly StyledProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextTrimmingProperty = + public static readonly StyledProperty TextTrimmingProperty = TextBlock.TextTrimmingProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty LineHeightProperty = + public static readonly StyledProperty LineHeightProperty = TextBlock.LineHeightProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty MaxLinesProperty = + public static readonly StyledProperty MaxLinesProperty = TextBlock.MaxLinesProperty.AddOwner(); /// From b34095fda37618cde431112f0abc3be3056f5ac2 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 13 May 2022 23:53:26 +0200 Subject: [PATCH 40/70] Optimize memory usage of styles and fix ordering issue in devtools. --- src/Avalonia.Base/Styling/IStyleInstance.cs | 5 +++ .../Styling/PropertySetterInstance.cs | 10 +++--- ...e.cs => PropertySetterTemplateInstance.cs} | 31 ++++++++-------- src/Avalonia.Base/Styling/Setter.cs | 9 ----- src/Avalonia.Base/Styling/StyleInstance.cs | 35 ++++++++++--------- .../ViewModels/ControlDetailsViewModel.cs | 3 +- 6 files changed, 46 insertions(+), 47 deletions(-) rename src/Avalonia.Base/Styling/{PropertySetterLazyInstance.cs => PropertySetterTemplateInstance.cs} (79%) diff --git a/src/Avalonia.Base/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs index 8ddb989bc0..d4f7510eb3 100644 --- a/src/Avalonia.Base/Styling/IStyleInstance.cs +++ b/src/Avalonia.Base/Styling/IStyleInstance.cs @@ -14,6 +14,11 @@ namespace Avalonia.Styling /// IStyle Source { get; } + /// + /// Gets a value indicating whether this style has an activator. + /// + bool HasActivator { get; } + /// /// Gets a value indicating whether this style is active. /// diff --git a/src/Avalonia.Base/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs index 48f462d006..c4e8f47e67 100644 --- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs @@ -44,7 +44,7 @@ namespace Avalonia.Styling { if (hasActivator) { - if (_styledProperty is object) + if (_styledProperty is not null) { _subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger); } @@ -55,13 +55,15 @@ namespace Avalonia.Styling } else { - if (_styledProperty is object) + var target = (AvaloniaObject) _target; + + if (_styledProperty is not null) { - _subscription = _target.SetValue(_styledProperty!, _value, BindingPriority.Style); + _subscription = target.SetValue(_styledProperty!, _value, BindingPriority.Style); } else { - _target.SetValue(_directProperty!, _value); + target.SetValue(_directProperty!, _value); } } } diff --git a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs similarity index 79% rename from src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs index 92653d0064..0f6efef1be 100644 --- a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs @@ -11,42 +11,42 @@ namespace Avalonia.Styling /// evaluated. /// /// The target property type. - internal class PropertySetterLazyInstance : SingleSubscriberObservableBase>, + internal class PropertySetterTemplateInstance : SingleSubscriberObservableBase>, ISetterInstance { private readonly IStyleable _target; private readonly StyledPropertyBase? _styledProperty; private readonly DirectPropertyBase? _directProperty; - private readonly Func _valueFactory; + private readonly ITemplate _template; private BindingValue _value; private IDisposable? _subscription; private bool _isActive; - public PropertySetterLazyInstance( + public PropertySetterTemplateInstance( IStyleable target, StyledPropertyBase property, - Func valueFactory) + ITemplate template) { _target = target; _styledProperty = property; - _valueFactory = valueFactory; + _template = template; } - public PropertySetterLazyInstance( + public PropertySetterTemplateInstance( IStyleable target, DirectPropertyBase property, - Func valueFactory) + ITemplate template) { _target = target; _directProperty = property; - _valueFactory = valueFactory; + _template = template; } public void Start(bool hasActivator) { _isActive = !hasActivator; - if (_styledProperty is object) + if (_styledProperty is not null) { var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style; _subscription = _target.Bind(_styledProperty, this, priority); @@ -77,7 +77,7 @@ namespace Avalonia.Styling public override void Dispose() { - if (_subscription is object) + if (_subscription is not null) { var sub = _subscription; _subscription = null; @@ -85,7 +85,7 @@ namespace Avalonia.Styling } else if (_isActive) { - if (_styledProperty is object) + if (_styledProperty is not null) { _target.ClearValue(_styledProperty); } @@ -101,22 +101,21 @@ namespace Avalonia.Styling protected override void Subscribed() => PublishNext(); protected override void Unsubscribed() { } - private T GetValue() + private void EnsureTemplate() { if (_value.HasValue) { - return _value.Value; + return; } - _value = _valueFactory(); - return _value.Value; + _value = (T) _template.Build(); } private void PublishNext() { if (_isActive) { - GetValue(); + EnsureTemplate(); PublishNext(_value); } else diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index b4b3399022..d989bb0706 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/src/Avalonia.Base/Styling/Setter.cs @@ -1,9 +1,7 @@ using System; using Avalonia.Animation; using Avalonia.Data; -using Avalonia.Data.Core; using Avalonia.Metadata; -using Avalonia.Utilities; #nullable enable @@ -70,12 +68,5 @@ namespace Avalonia.Styling return Property.CreateSetterInstance(target, Value); } - - private struct SetterVisitorData - { - public IStyleable target; - public object? value; - public ISetterInstance? result; - } } } diff --git a/src/Avalonia.Base/Styling/StyleInstance.cs b/src/Avalonia.Base/Styling/StyleInstance.cs index 830cf49a0d..db96da6821 100644 --- a/src/Avalonia.Base/Styling/StyleInstance.cs +++ b/src/Avalonia.Base/Styling/StyleInstance.cs @@ -11,10 +11,10 @@ namespace Avalonia.Styling /// /// A which has been instanced on a control. /// - internal class StyleInstance : IStyleInstance, IStyleActivatorSink + internal sealed class StyleInstance : IStyleInstance, IStyleActivatorSink { - private readonly List? _setters; - private readonly List? _animations; + private readonly ISetterInstance[]? _setters; + private readonly IDisposable[]? _animations; private readonly IStyleActivator? _activator; private readonly Subject? _animationTrigger; @@ -30,41 +30,42 @@ namespace Avalonia.Styling _activator = activator; IsActive = _activator is null; - if (setters is object) + if (setters is not null) { var setterCount = setters.Count; - _setters = new List(setterCount); + _setters = new ISetterInstance[setterCount]; for (var i = 0; i < setterCount; ++i) { - _setters.Add(setters[i].Instance(Target)); + _setters[i] = setters[i].Instance(Target); } } - if (animations is object && target is Animatable animatable) + if (animations is not null && target is Animatable animatable) { var animationsCount = animations.Count; - _animations = new List(animationsCount); + _animations = new IDisposable[animationsCount]; _animationTrigger = new Subject(); for (var i = 0; i < animationsCount; ++i) { - _animations.Add(animations[i].Apply(animatable, null, _animationTrigger)); + _animations[i] = animations[i].Apply(animatable, null, _animationTrigger); } } } + public bool HasActivator => _activator is not null; public bool IsActive { get; private set; } public IStyle Source { get; } public IStyleable Target { get; } public void Start() { - var hasActivator = _activator is object; + var hasActivator = HasActivator; - if (_setters is object) + if (_setters is not null) { foreach (var setter in _setters) { @@ -76,7 +77,7 @@ namespace Avalonia.Styling { _activator!.Subscribe(this, 0); } - else if (_animationTrigger != null) + else if (_animationTrigger is not null) { _animationTrigger.OnNext(true); } @@ -84,7 +85,7 @@ namespace Avalonia.Styling public void Dispose() { - if (_setters is object) + if (_setters is not null) { foreach (var setter in _setters) { @@ -92,11 +93,11 @@ namespace Avalonia.Styling } } - if (_animations is object) + if (_animations is not null) { - foreach (var subscripion in _animations) + foreach (var subscription in _animations) { - subscripion.Dispose(); + subscription.Dispose(); } } @@ -111,7 +112,7 @@ namespace Avalonia.Styling _animationTrigger?.OnNext(value); - if (_setters is object) + if (_setters is not null) { if (IsActive) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index a1fd425571..e383c160e3 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -60,7 +60,8 @@ namespace Avalonia.Diagnostics.ViewModels var styleDiagnostics = styledElement.GetStyleDiagnostics(); - foreach (var appliedStyle in styleDiagnostics.AppliedStyles) + // We need to place styles without activator first, such styles will be overwritten by ones with activators. + foreach (var appliedStyle in styleDiagnostics.AppliedStyles.OrderBy(s => s.HasActivator)) { var styleSource = appliedStyle.Source; From 6c7dc426fcec124fcb2edb75be0ec1fa0bdb0afc Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 13 May 2022 23:59:09 +0200 Subject: [PATCH 41/70] Missing property changes. --- src/Avalonia.Base/DirectPropertyBase.cs | 5 ++--- src/Avalonia.Base/StyledPropertyBase.cs | 8 +++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 9c1ffce24c..efcb7dfecb 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -2,7 +2,6 @@ using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Styling; -using Avalonia.Utilities; namespace Avalonia { @@ -188,10 +187,10 @@ namespace Avalonia } else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) { - return new PropertySetterLazyInstance( + return new PropertySetterTemplateInstance( target, this, - () => (TValue)template.Build()); + template); } else { diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs index dd5eb703ea..da607720ff 100644 --- a/src/Avalonia.Base/StyledPropertyBase.cs +++ b/src/Avalonia.Base/StyledPropertyBase.cs @@ -1,9 +1,7 @@ using System; -using System.Diagnostics; using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Styling; -using Avalonia.Utilities; namespace Avalonia { @@ -12,7 +10,7 @@ namespace Avalonia /// public abstract class StyledPropertyBase : AvaloniaProperty, IStyledPropertyAccessor { - private bool _inherits; + private readonly bool _inherits; /// /// Initializes a new instance of the class. @@ -243,10 +241,10 @@ namespace Avalonia } else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) { - return new PropertySetterLazyInstance( + return new PropertySetterTemplateInstance( target, this, - () => (TValue)template.Build()); + template); } else { From 90b67c3b9f73f2dfc6698f5f43486b0367c986cf Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 14 May 2022 01:22:34 +0200 Subject: [PATCH 42/70] Modify test setup so actual framework classes are used. --- tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs index ef2586fcb7..ed4c78aa3e 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs @@ -91,16 +91,13 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Setter_Should_Apply_Value_Without_Activator_With_Style_Priority() { - var control = new Mock(); - var style = Mock.Of