diff --git a/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 2de6fac5ac..dffb9d791d 100644 --- a/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ /* Begin PBXFileReference section */ 379A4506214D0F6500CC143D /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../headers; 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 = ""; }; AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; 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 = ""; }; @@ -52,6 +53,7 @@ AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */, AB661C212148288600291242 /* common.h */, AB661C1F2148286E00291242 /* window.mm */, + 37C09D8A21581EF2006A6758 /* window.h */, AB00E4F62147CA920032A60A /* main.mm */, 37C09D8721580FE4006A6758 /* SystemDialogs.mm */, AB7A61F02147C815003C5833 /* Products */, diff --git a/src/Avalonia.Native.OSX/SystemDialogs.mm b/src/Avalonia.Native.OSX/SystemDialogs.mm index 7d77e4fa22..bb5fc60077 100644 --- a/src/Avalonia.Native.OSX/SystemDialogs.mm +++ b/src/Avalonia.Native.OSX/SystemDialogs.mm @@ -1,25 +1,56 @@ #include "common.h" +#include "window.h" class SystemDialogs : public ComSingleObject { - virtual void SelectFolderDialog (IAvnSystemDialogEvents* events, + virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle, + IAvnSystemDialogEvents* events, const char* title, const char* initialPath) { } - virtual void OpenFileDialog (IAvnSystemDialogEvents* events, + virtual void OpenFileDialog (IAvnWindow* parentWindowHandle, + IAvnSystemDialogEvents* events, bool allowMultiple, const char* title, const char* initialDirectory, const char* intialFile, const char* filters) { - events->OnCompleted(0, nullptr); + auto panel = [NSOpenPanel openPanel]; + + panel.allowsMultipleSelection = allowMultiple; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(parentWindowHandle); + + [panel beginSheet:windowBase->Window completionHandler:^(NSModalResponse result) { + if(result == NSFileHandlingPanelOKButton) + { + + } + + events->OnCompleted(0, nullptr); + }]; + } + else + { + [panel beginWithCompletionHandler:^(NSModalResponse result) { + if(result == NSFileHandlingPanelOKButton) + { + + } + + events->OnCompleted(0, nullptr); + }]; + } } - virtual void SaveFileDialog (IAvnSystemDialogEvents* events, + virtual void SaveFileDialog (IAvnWindow* parentWindowHandle, + IAvnSystemDialogEvents* events, const char* title, const char* initialDirectory, const char* intialFile, diff --git a/src/Avalonia.Native.OSX/window.h b/src/Avalonia.Native.OSX/window.h new file mode 100644 index 0000000000..ed8406bd5f --- /dev/null +++ b/src/Avalonia.Native.OSX/window.h @@ -0,0 +1,182 @@ +// +// window.h +// Avalonia.Native.OSX +// +// Created by Dan Walmsley on 23/09/2018. +// Copyright © 2018 Avalonia. All rights reserved. +// + +#ifndef window_h +#define window_h + +class WindowBaseImpl; + +@interface AvnView : NSView +-(AvnView*) initWithParent: (WindowBaseImpl*) parent; +-(NSEvent*) lastMouseDownEvent; +-(AvnPoint) translateLocalPoint:(AvnPoint)pt; +@end + +@interface AvnWindow : NSWindow +-(AvnWindow*) initWithParent: (WindowBaseImpl*) parent; +-(void) setCanBecomeKeyAndMain; +@end + +class WindowBaseImpl : public ComSingleObject +{ +public: + AvnView* View; + AvnWindow* Window; + ComPtr BaseEvents; + AvnPoint lastPositionSet; + WindowBaseImpl(IAvnWindowBaseEvents* events) + { + BaseEvents = events; + View = [[AvnView alloc] initWithParent:this]; + Window = [[AvnWindow alloc] initWithParent:this]; + + lastPositionSet.X = 100; + lastPositionSet.Y = 100; + + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + [Window setContentView: View]; + } + + virtual HRESULT Show() + { + SetPosition(lastPositionSet); + UpdateStyle(); + [Window makeKeyAndOrderFront:Window]; + return S_OK; + } + + virtual HRESULT Hide () + { + if(Window != nullptr) + { + [Window orderOut:Window]; + } + return S_OK; + } + + virtual HRESULT Close() + { + [Window close]; + return S_OK; + } + + virtual HRESULT GetClientSize(AvnSize* ret) + { + 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 GetScaling (double* ret) + { + if(ret == nullptr) + return E_POINTER; + + if(Window == nullptr) + { + *ret = 1; + return S_OK; + } + + *ret = [Window backingScaleFactor]; + return S_OK; + } + + virtual HRESULT Resize(double x, double y) + { + [Window setContentSize:NSSize{x, y}]; + return S_OK; + } + + virtual void Invalidate (AvnRect rect) + { + [View setNeedsDisplayInRect:[View frame]]; + } + + virtual void BeginMoveDrag () + { + auto lastEvent = [View lastMouseDownEvent]; + + if(lastEvent == nullptr) + { + return; + } + + [Window performWindowDragWithEvent:lastEvent]; + } + + + virtual HRESULT GetPosition (AvnPoint* ret) + { + 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 void SetPosition (AvnPoint point) + { + lastPositionSet = point; + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + } + + virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) + { + if(ret == nullptr) + { + return E_POINTER; + } + + point = ConvertPointY(point); + auto viewPoint = [Window convertPointFromScreen:ToNSPoint(point)]; + + *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; + + return S_OK; + } + + virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) + { + if(ret == nullptr) + { + return E_POINTER; + } + + auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); + auto cocoaScreenPoint = [Window convertPointToScreen:cocoaViewPoint]; + *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); + + return S_OK; + } + +protected: + virtual NSWindowStyleMask GetStyle() + { + return NSWindowStyleMaskBorderless; + } + + void UpdateStyle() + { + [Window setStyleMask:GetStyle()]; + } +}; + +#endif /* window_h */ diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index ce5923089c..4995483c71 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -1,174 +1,5 @@ #include "common.h" - -class WindowBaseImpl; - -@interface AvnView : NSView --(AvnView*) initWithParent: (WindowBaseImpl*) parent; --(NSEvent*) lastMouseDownEvent; --(AvnPoint) translateLocalPoint:(AvnPoint)pt; -@end - -@interface AvnWindow : NSWindow --(AvnWindow*) initWithParent: (WindowBaseImpl*) parent; --(void) setCanBecomeKeyAndMain; -@end - -class WindowBaseImpl : public ComSingleObject -{ -public: - AvnView* View; - AvnWindow* Window; - ComPtr BaseEvents; - AvnPoint lastPositionSet; - WindowBaseImpl(IAvnWindowBaseEvents* events) - { - BaseEvents = events; - View = [[AvnView alloc] initWithParent:this]; - Window = [[AvnWindow alloc] initWithParent:this]; - - lastPositionSet.X = 100; - lastPositionSet.Y = 100; - - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - [Window setContentView: View]; - } - - virtual HRESULT Show() - { - SetPosition(lastPositionSet); - UpdateStyle(); - [Window makeKeyAndOrderFront:Window]; - return S_OK; - } - - virtual HRESULT Hide () - { - if(Window != nullptr) - { - [Window orderOut:Window]; - } - return S_OK; - } - - virtual HRESULT Close() - { - [Window close]; - return S_OK; - } - - virtual HRESULT GetClientSize(AvnSize* ret) - { - 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 GetScaling (double* ret) - { - if(ret == nullptr) - return E_POINTER; - - if(Window == nullptr) - { - *ret = 1; - return S_OK; - } - - *ret = [Window backingScaleFactor]; - return S_OK; - } - - virtual HRESULT Resize(double x, double y) - { - [Window setContentSize:NSSize{x, y}]; - return S_OK; - } - - virtual void Invalidate (AvnRect rect) - { - [View setNeedsDisplayInRect:[View frame]]; - } - - virtual void BeginMoveDrag () - { - auto lastEvent = [View lastMouseDownEvent]; - - if(lastEvent == nullptr) - { - return; - } - - [Window performWindowDragWithEvent:lastEvent]; - } - - - virtual HRESULT GetPosition (AvnPoint* ret) - { - 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 void SetPosition (AvnPoint point) - { - lastPositionSet = point; - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; - } - - virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) - { - if(ret == nullptr) - { - return E_POINTER; - } - - point = ConvertPointY(point); - auto viewPoint = [Window convertPointFromScreen:ToNSPoint(point)]; - - *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; - - return S_OK; - } - - virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); - auto cocoaScreenPoint = [Window convertPointToScreen:cocoaViewPoint]; - *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); - - return S_OK; - } - -protected: - virtual NSWindowStyleMask GetStyle() - { - return NSWindowStyleMaskBorderless; - } - - void UpdateStyle() - { - [Window setStyleMask:GetStyle()]; - } -}; +#include "window.h" @implementation AvnView { diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs index 8a9464e79f..cb16ec1239 100644 --- a/src/Avalonia.Native/SystemDialogs.cs +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -20,37 +20,37 @@ namespace Avalonia.Native public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) { - using (var events = new SystemDialogEvents()) - { - if (dialog is OpenFileDialog ofd) - { - _native.OpenFileDialog(events, ofd.AllowMultiple, - ofd.Title, - ofd.InitialDirectory, - ofd.InitialFileName, - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); - } - else - { - _native.SaveFileDialog(events, - dialog.Title, - dialog.InitialDirectory, - dialog.InitialFileName, - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); - } + var events = new SystemDialogEvents(); - return events.Task; + if (dialog is OpenFileDialog ofd) + { + _native.OpenFileDialog((parent as WindowImpl).Native, + events, ofd.AllowMultiple, + ofd.Title, + ofd.InitialDirectory, + ofd.InitialFileName, + string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); } + else + { + _native.SaveFileDialog((parent as WindowImpl).Native, + events, + dialog.Title, + dialog.InitialDirectory, + dialog.InitialFileName, + string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + } + + return events.Task.ContinueWith(t => { events.Dispose(); return t.Result; }); } - public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) { - using (var events = new SystemDialogEvents()) - { - _native.SelectFolderDialog(events, dialog.Title, dialog.InitialDirectory); + var events = new SystemDialogEvents(); - return (await events.Task).FirstOrDefault(); - } + _native.SelectFolderDialog((parent as WindowImpl).Native, events, dialog.Title, dialog.InitialDirectory); + + return events.Task.ContinueWith(t => { events.Dispose(); return t.Result.FirstOrDefault(); }); } } diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 484fe3d514..301071ac25 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -24,6 +24,8 @@ namespace Avalonia.Native } } + public IAvnWindow Native => _native; + public IDisposable ShowDialog() { return null; diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 82404c1b8d..92997c1d82 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -153,18 +153,21 @@ AVNCOM(IAvnSystemDialogEvents, 0c) : virtual IUnknown AVNCOM(IAvnSystemDialogs, 0d) : virtual IUnknown { - virtual void SelectFolderDialog (IAvnSystemDialogEvents* events, - const char* title, - const char* initialPath) = 0; + virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle, + IAvnSystemDialogEvents* events, + const char* title, + const char* initialPath) = 0; - virtual void OpenFileDialog (IAvnSystemDialogEvents* events, + virtual void OpenFileDialog (IAvnWindow* parentWindowHandle, + IAvnSystemDialogEvents* events, bool allowMultiple, const char* title, const char* initialDirectory, const char* intialFile, const char* filters) = 0; - virtual void SaveFileDialog (IAvnSystemDialogEvents* events, + virtual void SaveFileDialog (IAvnWindow* parentWindowHandle, + IAvnSystemDialogEvents* events, const char* title, const char* initialDirectory, const char* intialFile,