From 991d9d81c7a3032e3df54a711df599927edcc9c2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 5 May 2022 10:23:58 +0100 Subject: [PATCH] Merge pull request #8078 from AvaloniaUI/osx-nswindow-refactor Osx nswindow refactor: Part 1 # Conflicts: # native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj # native/Avalonia.Native/src/OSX/automation.h # native/Avalonia.Native/src/OSX/automation.mm # native/Avalonia.Native/src/OSX/window.h # native/Avalonia.Native/src/OSX/window.mm --- .../project.pbxproj | 32 + .../Avalonia.Native/src/OSX/INSWindowHolder.h | 15 + .../src/OSX/IWindowStateChanged.h | 18 + native/Avalonia.Native/src/OSX/ResizeScope.h | 23 + native/Avalonia.Native/src/OSX/ResizeScope.mm | 17 + .../Avalonia.Native/src/OSX/SystemDialogs.mm | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 119 ++ .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 504 ++++++ native/Avalonia.Native/src/OSX/WindowImpl.h | 97 ++ native/Avalonia.Native/src/OSX/WindowImpl.mm | 539 +++++++ native/Avalonia.Native/src/OSX/app.mm | 12 - native/Avalonia.Native/src/OSX/common.h | 3 +- native/Avalonia.Native/src/OSX/cursor.mm | 1 - native/Avalonia.Native/src/OSX/main.mm | 6 +- native/Avalonia.Native/src/OSX/menu.h | 1 - native/Avalonia.Native/src/OSX/menu.mm | 7 +- .../Avalonia.Native/src/OSX/rendertarget.mm | 4 - native/Avalonia.Native/src/OSX/window.h | 46 +- native/Avalonia.Native/src/OSX/window.mm | 1374 +---------------- 19 files changed, 1434 insertions(+), 1385 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/INSWindowHolder.h create mode 100644 native/Avalonia.Native/src/OSX/IWindowStateChanged.h create mode 100644 native/Avalonia.Native/src/OSX/ResizeScope.h create mode 100644 native/Avalonia.Native/src/OSX/ResizeScope.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowBaseImpl.h create mode 100644 native/Avalonia.Native/src/OSX/WindowBaseImpl.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowImpl.h create mode 100644 native/Avalonia.Native/src/OSX/WindowImpl.mm 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 85fcf20034f..e8bc229b40e 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 @@ -7,6 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; + 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; + 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; + 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; + 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; + 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; + 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.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 */; }; @@ -33,6 +41,14 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = ""; }; @@ -124,6 +140,14 @@ 37C09D8721580FE4006A6758 /* SystemDialogs.mm */, AB7A61F02147C815003C5833 /* Products */, AB661C1C2148230E00291242 /* Frameworks */, + 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */, + 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */, + 18391BBB7782C296D424071F /* INSWindowHolder.h */, + 183919BF108EB72A029F7671 /* WindowImpl.mm */, + 18391CD090AA776E7E841AC9 /* WindowImpl.h */, + 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */, + 18391E45702740FE9DD69695 /* ResizeScope.mm */, + 1839171D898F9BFC1373631A /* ResizeScope.h */, ); sourceTree = ""; }; @@ -143,6 +167,11 @@ buildActionMask = 2147483647; files = ( 37155CE4233C00EB0034DCE9 /* menu.h in Headers */, + 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */, + 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */, + 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */, + 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */, + 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -221,6 +250,9 @@ 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 */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h new file mode 100644 index 00000000000..aa8c34ef004 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -0,0 +1,15 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H +#define AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H + +struct INSWindowHolder +{ + virtual AvnWindow* _Nonnull GetNSWindow () = 0; + virtual AvnView* _Nonnull GetNSView () = 0; +}; + +#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H diff --git a/native/Avalonia.Native/src/OSX/IWindowStateChanged.h b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h new file mode 100644 index 00000000000..f0905da3ac4 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h @@ -0,0 +1,18 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H +#define AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H + +struct IWindowStateChanged +{ + virtual void WindowStateChanged () = 0; + virtual void StartStateTransition () = 0; + virtual void EndStateTransition () = 0; + virtual SystemDecorations Decorations () = 0; + virtual AvnWindowState WindowState () = 0; +}; + +#endif //AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h new file mode 100644 index 00000000000..c57dc966907 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/ResizeScope.h @@ -0,0 +1,23 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H +#define AVALONIA_NATIVE_OSX_RESIZESCOPE_H + +#include "window.h" +#include "avalonia-native.h" + +class ResizeScope +{ +public: + ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason); + + ~ResizeScope(); +private: + AvnView* _Nonnull _view; + AvnPlatformResizeReason _restore; +}; + +#endif //AVALONIA_NATIVE_OSX_RESIZESCOPE_H diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm new file mode 100644 index 00000000000..8644b41fba5 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm @@ -0,0 +1,17 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#include "ResizeScope.h" + +ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { + _view = view; + _restore = [view getResizeReason]; + [view setResizeReason:reason]; +} + +ResizeScope::~ResizeScope() { + [_view setResizeReason:_restore]; +} diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm index a47221056b1..21ad9cfa7c6 100644 --- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm +++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm @@ -1,5 +1,6 @@ #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 new file mode 100644 index 00000000000..94175b8187d --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -0,0 +1,119 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H +#define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H + +#import "rendertarget.h" +#include "INSWindowHolder.h" + +class WindowBaseImpl : public virtual ComObject, + public virtual IAvnWindowBase, + public INSWindowHolder { +private: + NSCursor *cursor; + +public: + FORWARD_IUNKNOWN() + +BEGIN_INTERFACE_MAP() + INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) + END_INTERFACE_MAP() + + virtual ~WindowBaseImpl() { + View = NULL; + Window = NULL; + } + + AutoFitContentView *StandardContainer; + AvnView *View; + AvnWindow *Window; + ComPtr BaseEvents; + ComPtr _glContext; + NSObject *renderTarget; + AvnPoint lastPositionSet; + NSString *_lastTitle; + + bool _shown; + bool _inResize; + + WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl); + + virtual HRESULT ObtainNSWindowHandle(void **ret) override; + + virtual HRESULT ObtainNSWindowHandleRetained(void **ret) override; + + virtual HRESULT ObtainNSViewHandle(void **ret) override; + + virtual HRESULT ObtainNSViewHandleRetained(void **ret) override; + + virtual AvnWindow *GetNSWindow() override; + + virtual AvnView *GetNSView() override; + + virtual HRESULT Show(bool activate, bool isDialog) override; + + virtual bool ShouldTakeFocusOnShow(); + + virtual HRESULT Hide() override; + + virtual HRESULT Activate() override; + + virtual HRESULT SetTopMost(bool value) override; + + virtual HRESULT Close() override; + + virtual HRESULT GetClientSize(AvnSize *ret) override; + + virtual HRESULT GetFrameSize(AvnSize *ret) override; + + virtual HRESULT GetScaling(double *ret) override; + + virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) override; + + virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override; + + virtual HRESULT Invalidate(__attribute__((unused)) AvnRect rect) override; + + virtual HRESULT SetMainMenu(IAvnMenu *menu) override; + + virtual HRESULT BeginMoveDrag() override; + + virtual HRESULT BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) override; + + virtual HRESULT GetPosition(AvnPoint *ret) override; + + virtual HRESULT SetPosition(AvnPoint point) override; + + virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override; + + virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override; + + virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) override; + + virtual HRESULT SetCursor(IAvnCursor *cursor) override; + + virtual void UpdateCursor(); + + virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) override; + + virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override; + + virtual HRESULT SetBlurEnabled(bool enable) override; + + virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, + IAvnClipboard *clipboard, IAvnDndResultCallback *cb, + void *sourceHandle) override; + + virtual bool IsDialog(); + +protected: + virtual NSWindowStyleMask GetStyle(); + + void UpdateStyle(); + +}; + +#endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm new file mode 100644 index 00000000000..59d463dc993 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -0,0 +1,504 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#include "common.h" +#import "window.h" +#include "menu.h" +#include "rendertarget.h" +#import "WindowBaseImpl.h" +#import "cursor.h" +#include "ResizeScope.h" + +WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { + _shown = false; + _inResize = false; + BaseEvents = events; + _glContext = gl; + renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl]; + View = [[AvnView alloc] initWithParent:this]; + StandardContainer = [[AutoFitContentView new] initWithContent:View]; + + Window = [[AvnWindow alloc] initWithParent:this]; + [Window setContentView:StandardContainer]; + + lastPositionSet.X = 100; + lastPositionSet.Y = 100; + _lastTitle = @""; + + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setOpaque:false]; +} + +HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge void *) View; + + return S_OK; +} + +HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge_retained void *) View; + + return S_OK; +} + +AvnWindow *WindowBaseImpl::GetNSWindow() { + return Window; +} + +AvnView *WindowBaseImpl::GetNSView() { + return View; +} + +HRESULT WindowBaseImpl::ObtainNSWindowHandleRetained(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge_retained void *) Window; + + return S_OK; +} + +HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { + START_COM_CALL; + + @autoreleasepool { + SetPosition(lastPositionSet); + UpdateStyle(); + + [Window setTitle:_lastTitle]; + + if (ShouldTakeFocusOnShow() && activate) { + [Window orderFront:Window]; + [Window makeKeyAndOrderFront:Window]; + [Window makeFirstResponder:View]; + [NSApp activateIgnoringOtherApps:YES]; + } else { + [Window orderFront:Window]; + } + + _shown = true; + + return S_OK; + } +} + +bool WindowBaseImpl::ShouldTakeFocusOnShow() { + return true; +} + +HRESULT WindowBaseImpl::ObtainNSWindowHandle(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge void *) Window; + + return S_OK; +} + +HRESULT WindowBaseImpl::Hide() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + [Window orderOut:Window]; + [Window restoreParentWindow]; + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Activate() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + [Window makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; + } + } + + return S_OK; +} + +HRESULT WindowBaseImpl::SetTopMost(bool value) { + START_COM_CALL; + + @autoreleasepool { + [Window setLevel:value ? NSFloatingWindowLevel : NSNormalWindowLevel]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Close() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + auto window = Window; + Window = nullptr; + + try { + // Seems to throw sometimes on application exit. + [window close]; + } + catch (NSException *) {} + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + auto frame = [View frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + auto frame = [Window frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetScaling(double *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + if (Window == nullptr) { + *ret = 1; + return S_OK; + } + + *ret = [Window backingScaleFactor]; + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { + START_COM_CALL; + + @autoreleasepool { + [Window setMinSize:ToNSSize(minSize)]; + [Window setMaxSize:ToNSSize(maxSize)]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reason) { + if (_inResize) { + return S_OK; + } + + _inResize = true; + + START_COM_CALL; + auto resizeBlock = ResizeScope(View, reason); + + @autoreleasepool { + auto maxSize = [Window maxSize]; + auto minSize = [Window minSize]; + + if (x < minSize.width) { + x = minSize.width; + } + + if (y < minSize.height) { + y = minSize.height; + } + + if (x > maxSize.width) { + x = maxSize.width; + } + + if (y > maxSize.height) { + y = maxSize.height; + } + + @try { + if (!_shown) { + BaseEvents->Resized(AvnSize{x, y}, reason); + } + + [Window setContentSize:NSSize{x, y}]; + [Window invalidateShadow]; + } + @finally { + _inResize = false; + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Invalidate(__attribute__((unused)) AvnRect rect) { + START_COM_CALL; + + @autoreleasepool { + [View setNeedsDisplayInRect:[View frame]]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { + START_COM_CALL; + + auto nativeMenu = dynamic_cast(menu); + + auto nsmenu = nativeMenu->GetNative(); + + [Window applyMenu:nsmenu]; + + if ([Window isKeyWindow]) { + [Window showWindowMenuWithAppMenu]; + } + + return S_OK; +} + +HRESULT WindowBaseImpl::BeginMoveDrag() { + START_COM_CALL; + + @autoreleasepool { + auto lastEvent = [View lastMouseDownEvent]; + + if (lastEvent == nullptr) { + return S_OK; + } + + [Window performWindowDragWithEvent:lastEvent]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) { + START_COM_CALL; + + return S_OK; +} + +HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + auto frame = [Window frame]; + + ret->X = frame.origin.x; + ret->Y = frame.origin.y + frame.size.height; + + *ret = ConvertPointY(*ret); + + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetPosition(AvnPoint point) { + START_COM_CALL; + + @autoreleasepool { + lastPositionSet = point; + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::PointToClient(AvnPoint point, AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + point = ConvertPointY(point); + NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; + auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); + + *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); + NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)]; + auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y)); + *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); + + return S_OK; + } +} + +HRESULT WindowBaseImpl::ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) { + START_COM_CALL; + + [View setSwRenderedFrame:fb dispose:dispose]; + return S_OK; +} + +HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) { + START_COM_CALL; + + @autoreleasepool { + Cursor *avnCursor = dynamic_cast(cursor); + this->cursor = avnCursor->GetNative(); + UpdateCursor(); + + if (avnCursor->IsHidden()) { + [NSCursor hide]; + } else { + [NSCursor unhide]; + } + + return S_OK; + } +} + +void WindowBaseImpl::UpdateCursor() { + if (cursor != nil) { + [cursor set]; + } +} + +HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) { + START_COM_CALL; + + if (View == NULL) + return E_FAIL; + *ppv = [renderTarget createSurfaceRenderTarget]; + return static_cast(*ppv == nil ? E_FAIL : S_OK); +} + +HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { + START_COM_CALL; + + if (View == NULL) + return E_FAIL; + *retOut = ::CreateNativeControlHost(View); + return S_OK; +} + +HRESULT WindowBaseImpl::SetBlurEnabled(bool enable) { + START_COM_CALL; + + [StandardContainer ShowBlur:enable]; + + return S_OK; +} + +HRESULT WindowBaseImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) { + START_COM_CALL; + + auto item = TryGetPasteboardItem(clipboard); + [item setString:@"" forType:GetAvnCustomDataType()]; + if (item == nil) + return E_INVALIDARG; + if (View == NULL) + return E_FAIL; + + auto nsevent = [NSApp currentEvent]; + auto nseventType = [nsevent type]; + + // If current event isn't a mouse one (probably due to malfunctioning user app) + // attempt to forge a new one + if (!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited) + || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) { + NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; + auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); + CGPoint cgpoint = NSPointToCGPoint(nspoint); + auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft); + nsevent = [NSEvent eventWithCGEvent:cgevent]; + CFRelease(cgevent); + } + + auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item]; + + auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments]; + NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height}; + [dragItem setDraggingFrame:dragItemRect contents:dragItemImage]; + + int op = 0; + int ieffects = (int) effects; + if ((ieffects & (int) AvnDragDropEffects::Copy) != 0) + op |= NSDragOperationCopy; + if ((ieffects & (int) AvnDragDropEffects::Link) != 0) + op |= NSDragOperationLink; + if ((ieffects & (int) AvnDragDropEffects::Move) != 0) + op |= NSDragOperationMove; + [View beginDraggingSessionWithItems:@[dragItem] event:nsevent + source:CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)]; + return S_OK; +} + +bool WindowBaseImpl::IsDialog() { + return false; +} + +NSWindowStyleMask WindowBaseImpl::GetStyle() { + return NSWindowStyleMaskBorderless; +} + +void WindowBaseImpl::UpdateStyle() { + [Window setStyleMask:GetStyle()]; +} \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h new file mode 100644 index 00000000000..b229921baa9 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -0,0 +1,97 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H +#define AVALONIA_NATIVE_OSX_WINDOWIMPL_H + + +#import "WindowBaseImpl.h" +#include "IWindowStateChanged.h" + +class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged +{ +private: + bool _canResize; + bool _fullScreenActive; + SystemDecorations _decorations; + AvnWindowState _lastWindowState; + AvnWindowState _actualWindowState; + bool _inSetWindowState; + NSRect _preZoomSize; + bool _transitioningWindowState; + bool _isClientAreaExtended; + bool _isDialog; + AvnExtendClientAreaChromeHints _extendClientHints; + + FORWARD_IUNKNOWN() +BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow) + END_INTERFACE_MAP() + virtual ~WindowImpl() + { + } + + ComPtr WindowEvents; + + WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl); + + void HideOrShowTrafficLights (); + + virtual HRESULT Show (bool activate, bool isDialog) override; + + virtual HRESULT SetEnabled (bool enable) override; + + virtual HRESULT SetParent (IAvnWindow* parent) override; + + void StartStateTransition () override ; + + void EndStateTransition () override ; + + SystemDecorations Decorations () override ; + + AvnWindowState WindowState () override ; + + void WindowStateChanged () override ; + + bool UndecoratedIsMaximized (); + + bool IsZoomed (); + + void DoZoom(); + + virtual HRESULT SetCanResize(bool value) override; + + virtual HRESULT SetDecorations(SystemDecorations value) override; + + virtual HRESULT SetTitle (char* utf8title) override; + + virtual HRESULT SetTitleBarColor(AvnColor color) override; + + virtual HRESULT GetWindowState (AvnWindowState*ret) override; + + virtual HRESULT TakeFocusFromChildren () override; + + virtual HRESULT SetExtendClientArea (bool enable) override; + + virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override; + + virtual HRESULT GetExtendTitleBarHeight (double*ret) override; + + virtual HRESULT SetExtendTitleBarHeight (double value) override; + + void EnterFullScreenMode (); + + void ExitFullScreenMode (); + + virtual HRESULT SetWindowState (AvnWindowState state) override; + + virtual bool IsDialog() override; + +protected: + virtual NSWindowStyleMask GetStyle() override; +}; + +#endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm new file mode 100644 index 00000000000..542a99fd44f --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -0,0 +1,539 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import "window.h" +#include "menu.h" +#import "WindowImpl.h" + + +WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { + _isClientAreaExtended = false; + _extendClientHints = AvnDefaultChrome; + _fullScreenActive = false; + _canResize = true; + _decorations = SystemDecorationsFull; + _transitioningWindowState = false; + _inSetWindowState = false; + _lastWindowState = Normal; + _actualWindowState = Normal; + WindowEvents = events; + [Window setCanBecomeKeyAndMain]; + [Window disableCursorRects]; + [Window setTabbingMode:NSWindowTabbingModeDisallowed]; + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; +} + +void WindowImpl::HideOrShowTrafficLights() { + if (Window == nil) { + return; + } + + for (id subview in Window.contentView.superview.subviews) { + if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { + NSView *titlebarView = [subview subviews][0]; + for (id button in titlebarView.subviews) { + if ([button isKindOfClass:[NSButton class]]) { + if (_isClientAreaExtended) { + auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + + [button setHidden:!wantsChrome]; + } else { + [button setHidden:(_decorations != SystemDecorationsFull)]; + } + + [button setWantsLayer:true]; + } + } + } + } +} + +HRESULT WindowImpl::Show(bool activate, bool isDialog) { + START_COM_CALL; + + @autoreleasepool { + _isDialog = isDialog; + WindowBaseImpl::Show(activate, isDialog); + + HideOrShowTrafficLights(); + + return SetWindowState(_lastWindowState); + } +} + +HRESULT WindowImpl::SetEnabled(bool enable) { + START_COM_CALL; + + @autoreleasepool { + [Window setEnabled:enable]; + return S_OK; + } +} + +HRESULT WindowImpl::SetParent(IAvnWindow *parent) { + START_COM_CALL; + + @autoreleasepool { + if (parent == nullptr) + return E_POINTER; + + auto cparent = dynamic_cast(parent); + if (cparent == nullptr) + return E_INVALIDARG; + + // If one tries to show a child window with a minimized parent window, then the parent window will be + // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive + // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. + if (cparent->WindowState() == Minimized) + cparent->SetWindowState(Normal); + + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; + [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; + + UpdateStyle(); + + return S_OK; + } +} + +void WindowImpl::StartStateTransition() { + _transitioningWindowState = true; +} + +void WindowImpl::EndStateTransition() { + _transitioningWindowState = false; +} + +SystemDecorations WindowImpl::Decorations() { + return _decorations; +} + +AvnWindowState WindowImpl::WindowState() { + return _lastWindowState; +} + +void WindowImpl::WindowStateChanged() { + if (_shown && !_inSetWindowState && !_transitioningWindowState) { + AvnWindowState state; + GetWindowState(&state); + + if (_lastWindowState != state) { + if (_isClientAreaExtended) { + if (_lastWindowState == FullScreen) { + // we exited fs. + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } + + [Window setTitlebarAppearsTransparent:true]; + + [StandardContainer setFrameSize:StandardContainer.frame.size]; + } else if (state == FullScreen) { + // we entered fs. + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = nullptr; + } + + [Window setTitlebarAppearsTransparent:false]; + + [StandardContainer setFrameSize:StandardContainer.frame.size]; + } + } + + _lastWindowState = state; + _actualWindowState = state; + WindowEvents->WindowStateChanged(state); + } + } +} + +bool WindowImpl::UndecoratedIsMaximized() { + auto windowSize = [Window frame]; + auto available = [Window screen].visibleFrame; + return CGRectEqualToRect(windowSize, available); +} + +bool WindowImpl::IsZoomed() { + return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized(); +} + +void WindowImpl::DoZoom() { + switch (_decorations) { + case SystemDecorationsNone: + case SystemDecorationsBorderOnly: + [Window setFrame:[Window screen].visibleFrame display:true]; + break; + + + case SystemDecorationsFull: + [Window performZoom:Window]; + break; + } +} + +HRESULT WindowImpl::SetCanResize(bool value) { + START_COM_CALL; + + @autoreleasepool { + _canResize = value; + UpdateStyle(); + return S_OK; + } +} + +HRESULT WindowImpl::SetDecorations(SystemDecorations value) { + START_COM_CALL; + + @autoreleasepool { + auto currentWindowState = _lastWindowState; + _decorations = value; + + if (_fullScreenActive) { + return S_OK; + } + + UpdateStyle(); + + HideOrShowTrafficLights(); + + switch (_decorations) { + case SystemDecorationsNone: + [Window setHasShadow:NO]; + [Window setTitleVisibility:NSWindowTitleHidden]; + [Window setTitlebarAppearsTransparent:YES]; + + if (currentWindowState == Maximized) { + if (!UndecoratedIsMaximized()) { + DoZoom(); + } + } + break; + + case SystemDecorationsBorderOnly: + [Window setHasShadow:YES]; + [Window setTitleVisibility:NSWindowTitleHidden]; + [Window setTitlebarAppearsTransparent:YES]; + + if (currentWindowState == Maximized) { + if (!UndecoratedIsMaximized()) { + DoZoom(); + } + } + break; + + case SystemDecorationsFull: + [Window setHasShadow:YES]; + [Window setTitleVisibility:NSWindowTitleVisible]; + [Window setTitlebarAppearsTransparent:NO]; + [Window setTitle:_lastTitle]; + + if (currentWindowState == Maximized) { + auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; + + [View setFrameSize:newFrame]; + } + break; + } + + return S_OK; + } +} + +HRESULT WindowImpl::SetTitle(char *utf8title) { + START_COM_CALL; + + @autoreleasepool { + _lastTitle = [NSString stringWithUTF8String:(const char *) utf8title]; + [Window setTitle:_lastTitle]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetTitleBarColor(AvnColor color) { + START_COM_CALL; + + @autoreleasepool { + float a = (float) color.Alpha / 255.0f; + float r = (float) color.Red / 255.0f; + float g = (float) color.Green / 255.0f; + float b = (float) color.Blue / 255.0f; + + auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; + + // Based on the titlebar color we have to choose either light or dark + // OSX doesnt let you set a foreground color for titlebar. + if ((r * 0.299 + g * 0.587 + b * 0.114) > 186.0f / 255.0f) { + [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; + } else { + [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; + } + + [Window setTitlebarAppearsTransparent:true]; + [Window setBackgroundColor:nscolor]; + } + + return S_OK; +} + +HRESULT WindowImpl::GetWindowState(AvnWindowState *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + if (([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) { + *ret = FullScreen; + return S_OK; + } + + if ([Window isMiniaturized]) { + *ret = Minimized; + return S_OK; + } + + if (IsZoomed()) { + *ret = Maximized; + return S_OK; + } + + *ret = Normal; + + return S_OK; + } +} + +HRESULT WindowImpl::TakeFocusFromChildren() { + START_COM_CALL; + + @autoreleasepool { + if (Window == nil) + return S_OK; + if ([Window isKeyWindow]) + [Window makeFirstResponder:View]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendClientArea(bool enable) { + START_COM_CALL; + + @autoreleasepool { + _isClientAreaExtended = enable; + + if (enable) { + Window.titleVisibility = NSWindowTitleHidden; + + [Window setTitlebarAppearsTransparent:true]; + + auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + + if (wantsTitleBar) { + [StandardContainer ShowTitleBar:true]; + } else { + [StandardContainer ShowTitleBar:false]; + } + + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } else { + Window.toolbar = nullptr; + } + } else { + Window.titleVisibility = NSWindowTitleVisible; + Window.toolbar = nullptr; + [Window setTitlebarAppearsTransparent:false]; + View.layer.zPosition = 0; + } + + [Window setIsExtended:enable]; + + HideOrShowTrafficLights(); + + UpdateStyle(); + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendClientAreaHints(AvnExtendClientAreaChromeHints hints) { + START_COM_CALL; + + @autoreleasepool { + _extendClientHints = hints; + + SetExtendClientArea(_isClientAreaExtended); + return S_OK; + } +} + +HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + *ret = [Window getExtendedTitleBarHeight]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendTitleBarHeight(double value) { + START_COM_CALL; + + @autoreleasepool { + [StandardContainer SetTitleBarHeightHint:value]; + return S_OK; + } +} + +void WindowImpl::EnterFullScreenMode() { + _fullScreenActive = true; + + [Window setTitle:_lastTitle]; + [Window toggleFullScreen:nullptr]; +} + +void WindowImpl::ExitFullScreenMode() { + [Window toggleFullScreen:nullptr]; + + _fullScreenActive = false; + + SetDecorations(_decorations); +} + +HRESULT WindowImpl::SetWindowState(AvnWindowState state) { + START_COM_CALL; + + @autoreleasepool { + if (Window == nullptr) { + return S_OK; + } + + if (_actualWindowState == state) { + return S_OK; + } + + _inSetWindowState = true; + + auto currentState = _actualWindowState; + _lastWindowState = state; + + if (currentState == Normal) { + _preZoomSize = [Window frame]; + } + + if (_shown) { + switch (state) { + case Maximized: + if (currentState == FullScreen) { + ExitFullScreenMode(); + } + + lastPositionSet.X = 0; + lastPositionSet.Y = 0; + + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + if (!IsZoomed()) { + DoZoom(); + } + break; + + case Minimized: + if (currentState == FullScreen) { + ExitFullScreenMode(); + } else { + [Window miniaturize:Window]; + } + break; + + case FullScreen: + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + EnterFullScreenMode(); + break; + + case Normal: + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + if (currentState == FullScreen) { + ExitFullScreenMode(); + } + + if (IsZoomed()) { + if (_decorations == SystemDecorationsFull) { + DoZoom(); + } else { + [Window setFrame:_preZoomSize display:true]; + auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; + + [View setFrameSize:newFrame]; + } + + } + break; + } + + _actualWindowState = _lastWindowState; + WindowEvents->WindowStateChanged(_actualWindowState); + } + + + _inSetWindowState = false; + + return S_OK; + } +} + +bool WindowImpl::IsDialog() { + return _isDialog; +} + +NSWindowStyleMask WindowImpl::GetStyle() { + unsigned long s = NSWindowStyleMaskBorderless; + + switch (_decorations) { + case SystemDecorationsNone: + s = s | NSWindowStyleMaskFullSizeContentView; + break; + + case SystemDecorationsBorderOnly: + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView; + break; + + case SystemDecorationsFull: + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; + + if (_canResize) { + s = s | NSWindowStyleMaskResizable; + } + break; + } + + if ([Window parentWindow] == nullptr) { + s |= NSWindowStyleMaskMiniaturizable; + } + + if (_isClientAreaExtended) { + s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground; + } + return s; +} diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 05b129baca1..14f1f6888c1 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -82,18 +82,6 @@ - (void)sendEvent:(NSEvent *)event _isHandlingSendEvent = oldHandling; } } - -// This is needed for certain embedded controls -- (BOOL) isHandlingSendEvent -{ - return _isHandlingSendEvent; -} - -- (void)setHandlingSendEvent:(BOOL)handlingSendEvent -{ - _isHandlingSendEvent = handlingSendEvent; -} - @end extern void InitializeAvnApp(IAvnApplicationEvents* events) diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 126c9aa87b4..af3ffff63d2 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -27,7 +27,7 @@ extern IAvnMenuItem* CreateAppMenuItem(); extern IAvnMenuItem* CreateAppMenuItemSeparator(); extern IAvnApplicationCommands* CreateApplicationCommands(); extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent); -extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu); +extern void SetAppMenu(IAvnMenu *menu); extern void SetServicesMenu (IAvnMenu* menu); extern IAvnMenu* GetAppMenu (); extern NSMenuItem* GetAppMenuItem (); @@ -37,7 +37,6 @@ extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; extern NSPoint ToNSPoint (AvnPoint p); extern AvnPoint ToAvnPoint (NSPoint p); extern AvnPoint ConvertPointY (AvnPoint p); -extern CGFloat PrimaryDisplayHeight(); extern NSSize ToNSSize (AvnSize s); #ifdef DEBUG #define NSDebugLog(...) NSLog(__VA_ARGS__) diff --git a/native/Avalonia.Native/src/OSX/cursor.mm b/native/Avalonia.Native/src/OSX/cursor.mm index dc38294a189..8638a03531d 100644 --- a/native/Avalonia.Native/src/OSX/cursor.mm +++ b/native/Avalonia.Native/src/OSX/cursor.mm @@ -1,6 +1,5 @@ #include "common.h" #include "cursor.h" -#include class CursorFactory : public ComSingleObject { diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 69f2995847d..43e4d46c323 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -342,7 +342,7 @@ virtual HRESULT SetAppMenu (IAvnMenu* appMenu) override @autoreleasepool { - ::SetAppMenu(s_appTitle, appMenu); + ::SetAppMenu(appMenu); return S_OK; } } @@ -418,7 +418,3 @@ AvnPoint ConvertPointY (AvnPoint p) return p; } -CGFloat PrimaryDisplayHeight() -{ - return NSMaxY([[[NSScreen screens] firstObject] frame]); -} diff --git a/native/Avalonia.Native/src/OSX/menu.h b/native/Avalonia.Native/src/OSX/menu.h index 186fcf255b6..ce46ac11e08 100644 --- a/native/Avalonia.Native/src/OSX/menu.h +++ b/native/Avalonia.Native/src/OSX/menu.h @@ -31,7 +31,6 @@ class AvnAppMenuItem : public ComSingleObject NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem IAvnActionCallback* _callback; IAvnPredicateCallback* _predicate; - bool _isSeparator; bool _isCheckable; public: diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index 2dbe76bc6df..fd74edd7729 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -1,7 +1,7 @@ #include "common.h" #include "menu.h" -#include "window.h" +#import "window.h" #include "KeyTransform.h" #include #include /* For kVK_ constants, and TIS functions. */ @@ -74,8 +74,7 @@ - (void)didSelectItem:(nullable id)sender AvnAppMenuItem::AvnAppMenuItem(bool isSeparator) { _isCheckable = false; - _isSeparator = isSeparator; - + if(isSeparator) { _native = [NSMenuItem separatorItem]; @@ -460,7 +459,7 @@ - (void)menuDidClose:(NSMenu *)menu static IAvnMenu* s_appMenu = nullptr; static NSMenuItem* s_appMenuItem = nullptr; -extern void SetAppMenu (NSString* appName, IAvnMenu* menu) +extern void SetAppMenu(IAvnMenu *menu) { s_appMenu = menu; diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index dc5c24e41e5..266d0345d11 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -1,14 +1,10 @@ #include "common.h" #include "rendertarget.h" -#import #import #import -#include -#include #include #include -#include @interface IOSurfaceHolder : NSObject @end diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 1dc091a48d5..271dd2534e4 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -1,6 +1,10 @@ #ifndef window_h #define window_h +#import "avalonia-native.h" + +@class AvnMenu; + class WindowBaseImpl; @interface AvnView : NSView @@ -9,7 +13,7 @@ class WindowBaseImpl; -(AvnPoint) translateLocalPoint:(AvnPoint)pt; -(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; -(void) onClosed; --(AvnPixelSize) getPixelSize; + -(AvnPlatformResizeReason) getResizeReason; -(void) setResizeReason:(AvnPlatformResizeReason)reason; + (AvnPoint)toAvnPoint:(CGPoint)p; @@ -19,12 +23,11 @@ class WindowBaseImpl; -(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; -(void) ShowTitleBar: (bool) show; -(void) SetTitleBarHeightHint: (double) height; --(void) SetContent: (NSView* _Nonnull) content; + -(void) ShowBlur: (bool) show; @end @interface AvnWindow : NSWindow -+(void) closeAll; -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; @@ -33,44 +36,11 @@ class WindowBaseImpl; -(void) setEnabled: (bool) enable; -(void) showAppMenuOnly; -(void) showWindowMenuWithAppMenu; --(void) applyMenu:(NSMenu* _Nullable)menu; --(double) getScaling; +-(void) applyMenu:(AvnMenu* _Nullable)menu; + -(double) getExtendedTitleBarHeight; -(void) setIsExtended:(bool)value; -(bool) isDialog; @end -struct INSWindowHolder -{ - virtual AvnWindow* _Nonnull GetNSWindow () = 0; -}; - -struct IWindowStateChanged -{ - virtual void WindowStateChanged () = 0; - virtual void StartStateTransition () = 0; - virtual void EndStateTransition () = 0; - virtual SystemDecorations Decorations () = 0; - virtual AvnWindowState WindowState () = 0; -}; - -class ResizeScope -{ -public: - ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason) - { - _view = view; - _restore = [view getResizeReason]; - [view setResizeReason:reason]; - } - - ~ResizeScope() - { - [_view setResizeReason:_restore]; - } -private: - AvnView* _Nonnull _view; - AvnPlatformResizeReason _restore; -}; - #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 369326095b7..96c9bbe90b2 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,1279 +1,11 @@ +#import #include "common.h" -#include "window.h" +#import "window.h" #include "KeyTransform.h" -#include "cursor.h" #include "menu.h" -#include #include "rendertarget.h" - -class WindowBaseImpl : public virtual ComSingleObject, public INSWindowHolder -{ -private: - NSCursor* cursor; - -public: - FORWARD_IUNKNOWN() - virtual ~WindowBaseImpl() - { - View = NULL; - Window = NULL; - } - AutoFitContentView* StandardContainer; - AvnView* View; - AvnWindow* Window; - ComPtr BaseEvents; - ComPtr _glContext; - NSObject* renderTarget; - AvnPoint lastPositionSet; - NSString* _lastTitle; - IAvnMenu* _mainMenu; - - bool _shown; - bool _inResize; - - WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) - { - _shown = false; - _inResize = false; - _mainMenu = nullptr; - BaseEvents = events; - _glContext = gl; - renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl]; - View = [[AvnView alloc] initWithParent:this]; - StandardContainer = [[AutoFitContentView new] initWithContent:View]; - - Window = [[AvnWindow alloc] initWithParent:this]; - [Window setContentView: StandardContainer]; - - lastPositionSet.X = 100; - lastPositionSet.Y = 100; - _lastTitle = @""; - - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setOpaque:false]; - } - - virtual HRESULT ObtainNSWindowHandle(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge void*)Window; - - return S_OK; - } - - virtual HRESULT ObtainNSWindowHandleRetained(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge_retained void*)Window; - - return S_OK; - } - - virtual HRESULT ObtainNSViewHandle(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge void*)View; - - return S_OK; - } - - virtual HRESULT ObtainNSViewHandleRetained(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge_retained void*)View; - - return S_OK; - } - - virtual AvnWindow* GetNSWindow() override - { - return Window; - } - - virtual HRESULT Show(bool activate, bool isDialog) override - { - START_COM_CALL; - - @autoreleasepool - { - SetPosition(lastPositionSet); - UpdateStyle(); - - [Window setTitle:_lastTitle]; - - if(ShouldTakeFocusOnShow() && activate) - { - [Window orderFront: Window]; - [Window makeKeyAndOrderFront:Window]; - [Window makeFirstResponder:View]; - [NSApp activateIgnoringOtherApps:YES]; - } - else - { - [Window orderFront: Window]; - } - - _shown = true; - - return S_OK; - } - } - - virtual bool ShouldTakeFocusOnShow() - { - return true; - } - - virtual HRESULT Hide () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window != nullptr) - { - [Window orderOut:Window]; - [Window restoreParentWindow]; - } - - return S_OK; - } - } - - virtual HRESULT Activate () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window != nullptr) - { - [Window makeKeyAndOrderFront:nil]; - [NSApp activateIgnoringOtherApps:YES]; - } - } - - return S_OK; - } - - virtual HRESULT SetTopMost (bool value) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel]; - - return S_OK; - } - } - - virtual HRESULT Close() override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - auto window = Window; - Window = nullptr; - - try{ - // Seems to throw sometimes on application exit. - [window close]; - } - catch(NSException*){} - } - - return S_OK; - } - } - - virtual HRESULT GetClientSize(AvnSize* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - auto frame = [View frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; - - return S_OK; - } - } - - virtual HRESULT GetFrameSize(AvnSize* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - auto frame = [Window frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; - - return S_OK; - } - } - - virtual HRESULT GetScaling (double* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - if(Window == nullptr) - { - *ret = 1; - return S_OK; - } - - *ret = [Window backingScaleFactor]; - return S_OK; - } - } - - virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setMinSize: ToNSSize(minSize)]; - [Window setMaxSize: ToNSSize(maxSize)]; - - return S_OK; - } - } - - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - if(_inResize) - { - return S_OK; - } - - _inResize = true; - - START_COM_CALL; - auto resizeBlock = ResizeScope(View, reason); - - @autoreleasepool - { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; - - if (x < minSize.width) - { - x = minSize.width; - } - - if (y < minSize.height) - { - y = minSize.height; - } - - if (x > maxSize.width) - { - x = maxSize.width; - } - - if (y > maxSize.height) - { - y = maxSize.height; - } - - @try - { - if(!_shown) - { - BaseEvents->Resized(AvnSize{x,y}, reason); - } - - [Window setContentSize:NSSize{x,y}]; - [Window invalidateShadow]; - } - @finally - { - _inResize = false; - } - - return S_OK; - } - } - - virtual HRESULT Invalidate (AvnRect rect) override - { - START_COM_CALL; - - @autoreleasepool - { - [View setNeedsDisplayInRect:[View frame]]; - - return S_OK; - } - } - - virtual HRESULT SetMainMenu(IAvnMenu* menu) override - { - START_COM_CALL; - - _mainMenu = menu; - - auto nativeMenu = dynamic_cast(menu); - - auto nsmenu = nativeMenu->GetNative(); - - [Window applyMenu:nsmenu]; - - if ([Window isKeyWindow]) - { - [Window showWindowMenuWithAppMenu]; - } - - return S_OK; - } - - virtual HRESULT BeginMoveDrag () override - { - START_COM_CALL; - - @autoreleasepool - { - auto lastEvent = [View lastMouseDownEvent]; - - if(lastEvent == nullptr) - { - return S_OK; - } - - [Window performWindowDragWithEvent:lastEvent]; - - return S_OK; - } - } - - virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) override - { - START_COM_CALL; - - return S_OK; - } - - virtual HRESULT GetPosition (AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto frame = [Window frame]; - - ret->X = frame.origin.x; - ret->Y = frame.origin.y + frame.size.height; - - *ret = ConvertPointY(*ret); - - return S_OK; - } - } - - virtual HRESULT SetPosition (AvnPoint point) override - { - START_COM_CALL; - - @autoreleasepool - { - lastPositionSet = point; - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; - - return S_OK; - } - } - - virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - point = ConvertPointY(point); - auto viewPoint = [Window convertScreenToBase:ToNSPoint(point)]; - - *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; - - return S_OK; - } - } - - virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); - auto cocoaScreenPoint = [Window convertBaseToScreen:cocoaViewPoint]; - *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); - - return S_OK; - } - } - - virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) override - { - START_COM_CALL; - - [View setSwRenderedFrame: fb dispose: dispose]; - return S_OK; - } - - virtual HRESULT SetCursor(IAvnCursor* cursor) override - { - START_COM_CALL; - - @autoreleasepool - { - Cursor* avnCursor = dynamic_cast(cursor); - this->cursor = avnCursor->GetNative(); - UpdateCursor(); - - if(avnCursor->IsHidden()) - { - [NSCursor hide]; - } - else - { - [NSCursor unhide]; - } - - return S_OK; - } - } - - virtual void UpdateCursor() - { - if (cursor != nil) - { - [cursor set]; - } - } - - virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) override - { - START_COM_CALL; - - if(View == NULL) - return E_FAIL; - *ppv = [renderTarget createSurfaceRenderTarget]; - return *ppv == nil ? E_FAIL : S_OK; - } - - virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) override - { - START_COM_CALL; - - if(View == NULL) - return E_FAIL; - *retOut = ::CreateNativeControlHost(View); - return S_OK; - } - - virtual HRESULT SetBlurEnabled (bool enable) override - { - START_COM_CALL; - - [StandardContainer ShowBlur:enable]; - - return S_OK; - } - - virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, - IAvnClipboard* clipboard, IAvnDndResultCallback* cb, - void* sourceHandle) override - { - START_COM_CALL; - - auto item = TryGetPasteboardItem(clipboard); - [item setString:@"" forType:GetAvnCustomDataType()]; - if(item == nil) - return E_INVALIDARG; - if(View == NULL) - return E_FAIL; - - auto nsevent = [NSApp currentEvent]; - auto nseventType = [nsevent type]; - - // If current event isn't a mouse one (probably due to malfunctioning user app) - // attempt to forge a new one - if(!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited) - || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) - { - auto nspoint = [Window convertBaseToScreen: ToNSPoint(point)]; - CGPoint cgpoint = NSPointToCGPoint(nspoint); - auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft); - nsevent = [NSEvent eventWithCGEvent: cgevent]; - CFRelease(cgevent); - } - - auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: item]; - - auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments]; - NSRect dragItemRect = {(float)point.X, (float)point.Y, [dragItemImage size].width, [dragItemImage size].height}; - [dragItem setDraggingFrame: dragItemRect contents: dragItemImage]; - - int op = 0; int ieffects = (int)effects; - if((ieffects & (int)AvnDragDropEffects::Copy) != 0) - op |= NSDragOperationCopy; - if((ieffects & (int)AvnDragDropEffects::Link) != 0) - op |= NSDragOperationLink; - if((ieffects & (int)AvnDragDropEffects::Move) != 0) - op |= NSDragOperationMove; - [View beginDraggingSessionWithItems: @[dragItem] event: nsevent - source: CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)]; - return S_OK; - } - - virtual bool IsDialog() - { - return false; - } - -protected: - virtual NSWindowStyleMask GetStyle() - { - return NSWindowStyleMaskBorderless; - } - - void UpdateStyle() - { - [Window setStyleMask: GetStyle()]; - } - -public: - virtual void OnResized () - { - - } -}; - -class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged -{ -private: - bool _canResize; - bool _fullScreenActive; - SystemDecorations _decorations; - AvnWindowState _lastWindowState; - AvnWindowState _actualWindowState; - bool _inSetWindowState; - NSRect _preZoomSize; - bool _transitioningWindowState; - bool _isClientAreaExtended; - bool _isDialog; - AvnExtendClientAreaChromeHints _extendClientHints; - - FORWARD_IUNKNOWN() - BEGIN_INTERFACE_MAP() - INHERIT_INTERFACE_MAP(WindowBaseImpl) - INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow) - END_INTERFACE_MAP() - virtual ~WindowImpl() - { - } - - ComPtr WindowEvents; - WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) - { - _isClientAreaExtended = false; - _extendClientHints = AvnDefaultChrome; - _fullScreenActive = false; - _canResize = true; - _decorations = SystemDecorationsFull; - _transitioningWindowState = false; - _inSetWindowState = false; - _lastWindowState = Normal; - _actualWindowState = Normal; - WindowEvents = events; - [Window setCanBecomeKeyAndMain]; - [Window disableCursorRects]; - [Window setTabbingMode:NSWindowTabbingModeDisallowed]; - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - } - - void HideOrShowTrafficLights () - { - if (Window == nil) - { - return; - } - - for (id subview in Window.contentView.superview.subviews) { - if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { - NSView *titlebarView = [subview subviews][0]; - for (id button in titlebarView.subviews) { - if ([button isKindOfClass:[NSButton class]]) - { - if(_isClientAreaExtended) - { - auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - - [button setHidden: !wantsChrome]; - } - else - { - [button setHidden: (_decorations != SystemDecorationsFull)]; - } - - [button setWantsLayer:true]; - } - } - } - } - } - - virtual HRESULT Show (bool activate, bool isDialog) override - { - START_COM_CALL; - - @autoreleasepool - { - _isDialog = isDialog; - WindowBaseImpl::Show(activate, isDialog); - - HideOrShowTrafficLights(); - - return SetWindowState(_lastWindowState); - } - } - - virtual HRESULT SetEnabled (bool enable) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setEnabled:enable]; - return S_OK; - } - } - - virtual HRESULT SetParent (IAvnWindow* parent) override - { - START_COM_CALL; - - @autoreleasepool - { - if(parent == nullptr) - return E_POINTER; - - auto cparent = dynamic_cast(parent); - if(cparent == nullptr) - return E_INVALIDARG; - - // If one tries to show a child window with a minimized parent window, then the parent window will be - // restored but MacOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive - // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. - if (cparent->WindowState() == Minimized) - cparent->SetWindowState(Normal); - - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; - - UpdateStyle(); - - return S_OK; - } - } - - void StartStateTransition () override - { - _transitioningWindowState = true; - } - - void EndStateTransition () override - { - _transitioningWindowState = false; - } - - SystemDecorations Decorations () override - { - return _decorations; - } - - AvnWindowState WindowState () override - { - return _lastWindowState; - } - - void WindowStateChanged () override - { - if(_shown && !_inSetWindowState && !_transitioningWindowState) - { - AvnWindowState state; - GetWindowState(&state); - - if(_lastWindowState != state) - { - if(_isClientAreaExtended) - { - if(_lastWindowState == FullScreen) - { - // we exited fs. - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; - } - - [Window setTitlebarAppearsTransparent:true]; - - [StandardContainer setFrameSize: StandardContainer.frame.size]; - } - else if(state == FullScreen) - { - // we entered fs. - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = nullptr; - } - - [Window setTitlebarAppearsTransparent:false]; - - [StandardContainer setFrameSize: StandardContainer.frame.size]; - } - } - - _lastWindowState = state; - _actualWindowState = state; - WindowEvents->WindowStateChanged(state); - } - } - } - - bool UndecoratedIsMaximized () - { - auto windowSize = [Window frame]; - auto available = [Window screen].visibleFrame; - return CGRectEqualToRect(windowSize, available); - } - - bool IsZoomed () - { - return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized(); - } - - void DoZoom() - { - switch (_decorations) - { - case SystemDecorationsNone: - case SystemDecorationsBorderOnly: - [Window setFrame:[Window screen].visibleFrame display:true]; - break; - - - case SystemDecorationsFull: - [Window performZoom:Window]; - break; - } - } - - virtual HRESULT SetCanResize(bool value) override - { - START_COM_CALL; - - @autoreleasepool - { - _canResize = value; - UpdateStyle(); - return S_OK; - } - } - - virtual HRESULT SetDecorations(SystemDecorations value) override - { - START_COM_CALL; - - @autoreleasepool - { - auto currentWindowState = _lastWindowState; - _decorations = value; - - if(_fullScreenActive) - { - return S_OK; - } - - UpdateStyle(); - - HideOrShowTrafficLights(); - - switch (_decorations) - { - case SystemDecorationsNone: - [Window setHasShadow:NO]; - [Window setTitleVisibility:NSWindowTitleHidden]; - [Window setTitlebarAppearsTransparent:YES]; - - if(currentWindowState == Maximized) - { - if(!UndecoratedIsMaximized()) - { - DoZoom(); - } - } - break; - - case SystemDecorationsBorderOnly: - [Window setHasShadow:YES]; - [Window setTitleVisibility:NSWindowTitleHidden]; - [Window setTitlebarAppearsTransparent:YES]; - - if(currentWindowState == Maximized) - { - if(!UndecoratedIsMaximized()) - { - DoZoom(); - } - } - break; - - case SystemDecorationsFull: - [Window setHasShadow:YES]; - [Window setTitleVisibility:NSWindowTitleVisible]; - [Window setTitlebarAppearsTransparent:NO]; - [Window setTitle:_lastTitle]; - - if(currentWindowState == Maximized) - { - auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; - - [View setFrameSize:newFrame]; - } - break; - } - - return S_OK; - } - } - - virtual HRESULT SetTitle (char* utf8title) override - { - START_COM_CALL; - - @autoreleasepool - { - _lastTitle = [NSString stringWithUTF8String:(const char*)utf8title]; - [Window setTitle:_lastTitle]; - - return S_OK; - } - } - - virtual HRESULT SetTitleBarColor(AvnColor color) override - { - START_COM_CALL; - - @autoreleasepool - { - float a = (float)color.Alpha / 255.0f; - float r = (float)color.Red / 255.0f; - float g = (float)color.Green / 255.0f; - float b = (float)color.Blue / 255.0f; - - auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; - - // Based on the titlebar color we have to choose either light or dark - // OSX doesnt let you set a foreground color for titlebar. - if ((r*0.299 + g*0.587 + b*0.114) > 186.0f / 255.0f) - { - [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; - } - else - { - [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; - } - - [Window setTitlebarAppearsTransparent:true]; - [Window setBackgroundColor:nscolor]; - } - - return S_OK; - } - - virtual HRESULT GetWindowState (AvnWindowState*ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - if(([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) - { - *ret = FullScreen; - return S_OK; - } - - if([Window isMiniaturized]) - { - *ret = Minimized; - return S_OK; - } - - if(IsZoomed()) - { - *ret = Maximized; - return S_OK; - } - - *ret = Normal; - - return S_OK; - } - } - - virtual HRESULT TakeFocusFromChildren () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window == nil) - return S_OK; - if([Window isKeyWindow]) - [Window makeFirstResponder: View]; - - return S_OK; - } - } - - virtual HRESULT SetExtendClientArea (bool enable) override - { - START_COM_CALL; - - @autoreleasepool - { - _isClientAreaExtended = enable; - - if(enable) - { - Window.titleVisibility = NSWindowTitleHidden; - - [Window setTitlebarAppearsTransparent:true]; - - auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - - if (wantsTitleBar) - { - [StandardContainer ShowTitleBar:true]; - } - else - { - [StandardContainer ShowTitleBar:false]; - } - - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; - } - else - { - Window.toolbar = nullptr; - } - } - else - { - Window.titleVisibility = NSWindowTitleVisible; - Window.toolbar = nullptr; - [Window setTitlebarAppearsTransparent:false]; - View.layer.zPosition = 0; - } - - [Window setIsExtended:enable]; - - HideOrShowTrafficLights(); - - UpdateStyle(); - - return S_OK; - } - } - - virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override - { - START_COM_CALL; - - @autoreleasepool - { - _extendClientHints = hints; - - SetExtendClientArea(_isClientAreaExtended); - return S_OK; - } - } - - virtual HRESULT GetExtendTitleBarHeight (double*ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - *ret = [Window getExtendedTitleBarHeight]; - - return S_OK; - } - } - - virtual HRESULT SetExtendTitleBarHeight (double value) override - { - START_COM_CALL; - - @autoreleasepool - { - [StandardContainer SetTitleBarHeightHint:value]; - return S_OK; - } - } - - void EnterFullScreenMode () - { - _fullScreenActive = true; - - [Window setTitle:_lastTitle]; - [Window toggleFullScreen:nullptr]; - } - - void ExitFullScreenMode () - { - [Window toggleFullScreen:nullptr]; - - _fullScreenActive = false; - - SetDecorations(_decorations); - } - - virtual HRESULT SetWindowState (AvnWindowState state) override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window == nullptr) - { - return S_OK; - } - - if(_actualWindowState == state) - { - return S_OK; - } - - _inSetWindowState = true; - - auto currentState = _actualWindowState; - _lastWindowState = state; - - if(currentState == Normal) - { - _preZoomSize = [Window frame]; - } - - if(_shown) - { - switch (state) { - case Maximized: - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - - lastPositionSet.X = 0; - lastPositionSet.Y = 0; - - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - if(!IsZoomed()) - { - DoZoom(); - } - break; - - case Minimized: - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - else - { - [Window miniaturize:Window]; - } - break; - - case FullScreen: - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - EnterFullScreenMode(); - break; - - case Normal: - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - - if(IsZoomed()) - { - if(_decorations == SystemDecorationsFull) - { - DoZoom(); - } - else - { - [Window setFrame:_preZoomSize display:true]; - auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; - - [View setFrameSize:newFrame]; - } - - } - break; - } - - _actualWindowState = _lastWindowState; - WindowEvents->WindowStateChanged(_actualWindowState); - } - - - _inSetWindowState = false; - - return S_OK; - } - } - - virtual void OnResized () override - { - if(_shown && !_inSetWindowState && !_transitioningWindowState) - { - WindowStateChanged(); - } - } - - virtual bool IsDialog() override - { - return _isDialog; - } - -protected: - virtual NSWindowStyleMask GetStyle() override - { - unsigned long s = NSWindowStyleMaskBorderless; - - switch (_decorations) - { - case SystemDecorationsNone: - s = s | NSWindowStyleMaskFullSizeContentView; - break; - - case SystemDecorationsBorderOnly: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView; - break; - - case SystemDecorationsFull: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; - - if(_canResize) - { - s = s | NSWindowStyleMaskResizable; - } - break; - } - - if([Window parentWindow] == nullptr) - { - s |= NSWindowStyleMaskMiniaturizable; - } - - if(_isClientAreaExtended) - { - s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground; - } - return s; - } -}; - -NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; +#import "WindowBaseImpl.h" +#include "WindowImpl.h" @implementation AutoFitContentView { @@ -1370,26 +102,13 @@ -(void)setFrameSize:(NSSize)newSize _settingSize = false; } - --(void) SetContent: (NSView* _Nonnull) content -{ - if(content != nullptr) - { - [content removeFromSuperview]; - [self addSubview:content]; - _content = content; - } -} @end @implementation AvnView { ComPtr _parent; - ComPtr _swRenderedFrame; - AvnFramebuffer _swRenderedFrameBuffer; - bool _queuedDisplayFromThread; NSTrackingArea* _area; - bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed, _isMouseOver; + bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; AvnInputModifiers _modifierState; NSEvent* _lastMouseDownEvent; bool _lastKeyHandled; @@ -1406,11 +125,6 @@ - (void)onClosed } } --(AvnPixelSize) getPixelSize -{ - return _lastPixelSize; -} - - (NSEvent*) lastMouseDownEvent { return _lastMouseDownEvent; @@ -1418,7 +132,7 @@ - (NSEvent*) lastMouseDownEvent - (void) updateRenderTarget { - [_renderTarget resize:_lastPixelSize withScale: [[self window] backingScaleFactor]]; + [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; [self setNeedsDisplayInRect:[self frame]]; } @@ -1608,7 +322,7 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; auto avnPoint = [AvnView toAvnPoint:localPoint]; auto point = [self translateLocalPoint:avnPoint]; - AvnVector delta; + AvnVector delta = { 0, 0}; if(type == Wheel) { @@ -1627,8 +341,21 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type return; } } + else if (type == Magnify) + { + delta.X = delta.Y = [event magnification]; + } + else if (type == Rotate) + { + delta.X = delta.Y = [event rotation]; + } + else if (type == Swipe) + { + delta.X = [event deltaX]; + delta.Y = [event deltaY]; + } - auto timestamp = [event timestamp] * 1000; + uint32 timestamp = static_cast([event timestamp] * 1000); auto modifiers = [self getModifiers:[event modifierFlags]]; if(type != AvnRawMouseEventType::Move || @@ -1687,6 +414,9 @@ - (void)otherMouseDown:(NSEvent *)event _isXButton2Pressed = true; [self mouseEvent:event withType:XButton2Down]; break; + + default: + break; } } @@ -1720,6 +450,9 @@ - (void)otherMouseUp:(NSEvent *)event _isXButton2Pressed = false; [self mouseEvent:event withType:XButton2Up]; break; + + default: + break; } } @@ -1753,18 +486,34 @@ - (void)scrollWheel:(NSEvent *)event [super scrollWheel:event]; } +- (void)magnifyWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Magnify]; + [super magnifyWithEvent:event]; +} + +- (void)rotateWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Rotate]; + [super rotateWithEvent:event]; +} + +- (void)swipeWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Swipe]; + [super swipeWithEvent:event]; +} + - (void)mouseEntered:(NSEvent *)event { - _isMouseOver = true; [super mouseEntered:event]; } - (void)mouseExited:(NSEvent *)event { - _isMouseOver = false; [self mouseEvent:event withType:LeaveWindow]; [super mouseExited:event]; -} +} - (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type { @@ -1775,7 +524,7 @@ - (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type auto key = s_KeyMap[[event keyCode]]; - auto timestamp = [event timestamp] * 1000; + uint32_t timestamp = static_cast([event timestamp] * 1000); auto modifiers = [self getModifiers:[event modifierFlags]]; if(_parent != nullptr) @@ -1943,7 +692,7 @@ - (NSUInteger)characterIndexForPoint:(NSPoint)point - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange { - CGRect result; + CGRect result = { 0 }; return result; } @@ -1962,10 +711,10 @@ - (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id (0); // Ensure that the managed part didn't add any new effects - reffects = (int)effects & (int)reffects; + reffects = (int)effects & reffects; // OSX requires exactly one operation if((reffects & (int)AvnDragDropEffects::Copy) != 0) @@ -2014,8 +763,7 @@ - (AvnPlatformResizeReason)getResizeReason return _resizeReason; } -- (void)setResizeReason:(AvnPlatformResizeReason)reason -{ +- (void)setResizeReason:(AvnPlatformResizeReason)reason { _resizeReason = reason; } @@ -2030,7 +778,6 @@ @implementation AvnWindow bool _isEnabled; bool _isExtended; AvnMenu* _menu; - double _lastScaling; } -(void) setIsExtended:(bool)value; @@ -2043,11 +790,6 @@ -(bool) isDialog return _parent->IsDialog(); } --(double) getScaling -{ - return _lastScaling; -} - -(double) getExtendedTitleBarHeight { if(_isExtended) @@ -2070,11 +812,6 @@ -(double) getExtendedTitleBarHeight } } -+(void)closeAll -{ - [[NSApplication sharedApplication] terminate:self]; -} - - (void)performClose:(id)sender { if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) @@ -2182,7 +919,7 @@ -(AvnWindow*) initWithParent: (WindowBaseImpl*) parent _closed = false; _isEnabled = true; - _lastScaling = [self backingScaleFactor]; + [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; _isExtended = false; @@ -2203,7 +940,7 @@ - (BOOL)windowShouldClose:(NSWindow *)sender - (void)windowDidChangeBackingProperties:(NSNotification *)notification { - _lastScaling = [self backingScaleFactor]; + [self backingScaleFactor]; } - (void)windowWillClose:(NSNotification *)notification @@ -2420,9 +1157,9 @@ - (void)sendEvent:(NSEvent *)event { auto avnPoint = [AvnView toAvnPoint:windowPoint]; auto point = [self translateLocalPoint:avnPoint]; - AvnVector delta; + AvnVector delta = { 0, 0 }; - _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, [event timestamp] * 1000, AvnInputModifiersNone, point, delta); + _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } } break; @@ -2444,6 +1181,7 @@ - (void)sendEvent:(NSEvent *)event } } } + @end class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup