From bd7b3c463b93bc6c14f4386af2e5d28027b15031 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 16 Sep 2018 12:43:35 +0100 Subject: [PATCH 01/17] implement popups. --- src/Avalonia.Native.OSX/common.h | 1 + src/Avalonia.Native.OSX/main.mm | 11 +++++- src/Avalonia.Native.OSX/window.mm | 36 ++++++++++++++++++- src/Avalonia.Native/AvaloniaNativePlatform.cs | 4 +-- src/Avalonia.Native/PopupImpl.cs | 27 ++++++++++++++ src/Avalonia.Native/WindowImplBase.cs | 1 + src/headers/avalonia-native.h | 24 ++++++++----- 7 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 src/Avalonia.Native/PopupImpl.cs diff --git a/src/Avalonia.Native.OSX/common.h b/src/Avalonia.Native.OSX/common.h index 5fd7e57556..303298ba02 100644 --- a/src/Avalonia.Native.OSX/common.h +++ b/src/Avalonia.Native.OSX/common.h @@ -9,4 +9,5 @@ extern IAvnPlatformThreadingInterface* CreatePlatformThreading(); extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events); +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events); #endif diff --git a/src/Avalonia.Native.OSX/main.mm b/src/Avalonia.Native.OSX/main.mm index 0d6b2a978d..a04ed11a1e 100644 --- a/src/Avalonia.Native.OSX/main.mm +++ b/src/Avalonia.Native.OSX/main.mm @@ -80,11 +80,20 @@ public: return S_OK; }; + virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv) + { + if(cb == nullptr || ppv == nullptr) + return E_POINTER; + + *ppv = CreateAvnPopup(cb); + return S_OK; + } + virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) { *ppv = CreatePlatformThreading(); return S_OK; - }; + } }; extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative() diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index f69bec5704..9e2abfead1 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -35,6 +35,15 @@ public: return S_OK; } + virtual HRESULT Hide () + { + if(Window != nullptr) + { + [Window orderOut:Window]; + } + return S_OK; + } + virtual HRESULT Close() { [Window close]; @@ -368,6 +377,32 @@ protected: @end +class PopupImpl : public WindowBaseImpl, public IAvnPopup +{ +private: + BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) + END_INTERFACE_MAP() + ComPtr WindowEvents; + PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events) + { + WindowEvents = events; + [Window setLevel:NSPopUpMenuWindowLevel]; + } + +protected: + virtual NSWindowStyleMask GetStyle() + { + return NSWindowStyleMaskBorderless; + } +}; + +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events) +{ + IAvnPopup* ptr = dynamic_cast(new PopupImpl(events)); + return ptr; +} class WindowImpl : public WindowBaseImpl, public IAvnWindow { @@ -413,7 +448,6 @@ protected: } }; - extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events) { IAvnWindow* ptr = dynamic_cast(new WindowImpl(events)); diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index ed9acb1d6f..515f56167f 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -88,8 +88,8 @@ namespace Avalonia.Native } public IPopupImpl CreatePopup() - { - throw new NotImplementedException(); + { + return new PopupImpl(_factory); } } diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs new file mode 100644 index 0000000000..bed2b3f929 --- /dev/null +++ b/src/Avalonia.Native/PopupImpl.cs @@ -0,0 +1,27 @@ +using System; +using Avalonia.Native.Interop; +using Avalonia.Platform; + +namespace Avalonia.Native +{ + public class PopupImpl : WindowBaseImpl, IPopupImpl + { + IAvnPopup _native; + + public PopupImpl(IAvaloniaNativeFactory factory) + { + using (var e = new PopupEvents(this)) + Init(_native = factory.CreatePopup(e)); + } + + class PopupEvents : WindowBaseEvents, IAvnWindowEvents + { + readonly PopupImpl _parent; + + public PopupEvents(PopupImpl parent) : base(parent) + { + _parent = parent; + } + } + } +} diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 9f1a7deedb..b5defeda6a 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -197,6 +197,7 @@ namespace Avalonia.Native public void Hide() { + _native.Hide(); } public void BeginMoveDrag() diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 1509f01a57..110dc0f425 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -4,6 +4,7 @@ struct IAvnWindowEvents; struct IAvnWindow; +struct IAvnPopup; struct IAvnMacOptions; struct IAvnPlatformThreadingInterface; @@ -59,12 +60,14 @@ public: virtual HRESULT Initialize() = 0; virtual IAvnMacOptions* GetMacOptions() = 0; virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) = 0; + virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnPopup** ppv) = 0; virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0; }; AVNCOM(IAvnWindowBase, 02) : virtual IUnknown { virtual HRESULT Show() = 0; + virtual HRESULT Hide () = 0; virtual HRESULT Close() = 0; virtual HRESULT GetClientSize(AvnSize*ret) = 0; virtual HRESULT Resize(double width, double height) = 0; @@ -72,13 +75,18 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown virtual void BeginMoveDrag () = 0; }; -AVNCOM(IAvnWindow, 03) : virtual IAvnWindowBase +AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase +{ + +}; + +AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase { virtual HRESULT SetCanResize(bool value) = 0; virtual HRESULT SetHasDecorations(bool value) = 0; }; -AVNCOM(IAvnWindowBaseEvents, 04) : IUnknown +AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown { virtual HRESULT SoftwareDraw(void* ptr, int stride, int pixelWidth, int pixelHeight, const AvnSize& logicalSize) = 0; virtual void Closed() = 0; @@ -93,33 +101,33 @@ AVNCOM(IAvnWindowBaseEvents, 04) : IUnknown }; -AVNCOM(IAvnWindowEvents, 05) : IAvnWindowBaseEvents +AVNCOM(IAvnWindowEvents, 06) : IAvnWindowBaseEvents { }; -AVNCOM(IAvnMacOptions, 06) : virtual IUnknown +AVNCOM(IAvnMacOptions, 07) : virtual IUnknown { virtual HRESULT SetShowInDock(int show) = 0; }; -AVNCOM(IAvnActionCallback, 07) : IUnknown +AVNCOM(IAvnActionCallback, 08) : IUnknown { virtual void Run() = 0; }; -AVNCOM(IAvnSignaledCallback, 08) : IUnknown +AVNCOM(IAvnSignaledCallback, 09) : IUnknown { virtual void Signaled(int priority, bool priorityContainsMeaningfulValue) = 0; }; -AVNCOM(IAvnLoopCancellation, 09) : virtual IUnknown +AVNCOM(IAvnLoopCancellation, 0a) : virtual IUnknown { virtual void Cancel() = 0; }; -AVNCOM(IAvnPlatformThreadingInterface, 0a) : virtual IUnknown +AVNCOM(IAvnPlatformThreadingInterface, 0b) : virtual IUnknown { virtual bool GetCurrentThreadIsLoopThread() = 0; virtual void SetSignaledCallback(IAvnSignaledCallback* cb) = 0; From 1d6ba001503f71eb6a1000a8e26034b8288e0dca Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 16 Sep 2018 13:10:58 +0100 Subject: [PATCH 02/17] implement setting of window position. --- src/Avalonia.Native.OSX/window.mm | 32 ++++++++++++++++++++++ src/Avalonia.Native/Avalonia.Native.csproj | 8 ++++++ src/Avalonia.Native/Helpers.cs | 18 ++++++++++++ src/Avalonia.Native/WindowImplBase.cs | 12 ++++++-- src/headers/avalonia-native.h | 2 ++ 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/Avalonia.Native/Helpers.cs diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index 9e2abfead1..a35d6ba8df 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -83,6 +83,38 @@ public: [Window performWindowDragWithEvent:lastEvent]; } + + virtual AvnPoint GetPosition () + { + auto frame = [Window frame]; + + AvnPoint bottomLeft; + bottomLeft.X = frame.origin.x; + bottomLeft.Y = frame.origin.y + frame.size.width; + + NSRect screen = [NSScreen.screens objectAtIndex:0].frame; + + auto t = MAX(screen.origin.y, screen.origin.y + screen.size.height); + + bottomLeft.Y = t - bottomLeft.Y; + + return bottomLeft; + } + + virtual void SetPosition (AvnPoint point) + { + NSPoint nspoint; + nspoint.x = point.X; + nspoint.y = point.Y; + + NSRect screen = [NSScreen.screens objectAtIndex:0].frame; + auto t = MAX(screen.origin.y, screen.origin.y + screen.size.height); + + nspoint.y = t - nspoint.y; + + [Window setFrameTopLeftPoint:nspoint]; + } + protected: virtual NSWindowStyleMask GetStyle() { diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj index 9f699ab664..7b0eef0a92 100644 --- a/src/Avalonia.Native/Avalonia.Native.csproj +++ b/src/Avalonia.Native/Avalonia.Native.csproj @@ -12,4 +12,12 @@ + + + + + + + + diff --git a/src/Avalonia.Native/Helpers.cs b/src/Avalonia.Native/Helpers.cs new file mode 100644 index 0000000000..941dd1dd14 --- /dev/null +++ b/src/Avalonia.Native/Helpers.cs @@ -0,0 +1,18 @@ +using System; +using Avalonia.Native.Interop; + +namespace Avalonia.Native +{ + public static class Helpers + { + public static Point ToAvaloniaPoint (this AvnPoint pt) + { + return new Point(pt.X, pt.Y); + } + + public static AvnPoint ToAvnPoint (this Point pt) + { + return new AvnPoint { X = pt.X, Y = pt.Y }; + } + } +} diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index b5defeda6a..00d8aba27d 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -118,11 +118,11 @@ namespace Avalonia.Native switch (type) { case AvnRawMouseEventType.Wheel: - Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, new Point(point.X, point.Y), new Vector(delta.X, delta.Y), (InputModifiers)modifiers)); + Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (InputModifiers)modifiers)); break; default: - Input?.Invoke(new RawMouseEventArgs(_mouse, timeStamp, _inputRoot, (RawMouseEventType)type, new Point(point.X, point.Y), (InputModifiers)modifiers)); + Input?.Invoke(new RawMouseEventArgs(_mouse, timeStamp, _inputRoot, (RawMouseEventType)type, point.ToAvaloniaPoint(), (InputModifiers)modifiers)); break; } } @@ -164,10 +164,16 @@ namespace Avalonia.Native } + public Point Position + { + get => _native.GetPosition().ToAvaloniaPoint(); + set => _native.SetPosition(value.ToAvnPoint()); + } + + #region Stubs public double Scaling => 1; - public Point Position { get; set; } public Action PositionChanged { get; set; } public Action Deactivated { get; set; } public Action Activated { get; set; } diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 110dc0f425..d05f82045f 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -73,6 +73,8 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown virtual HRESULT Resize(double width, double height) = 0; virtual void Invalidate (AvnRect rect) = 0; virtual void BeginMoveDrag () = 0; + virtual AvnPoint GetPosition () = 0; + virtual void SetPosition (AvnPoint point) = 0; }; AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase From 1567075df5e96bdc1c6a322c5445ab9c400221a6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 16 Sep 2018 16:57:20 +0100 Subject: [PATCH 03/17] add helper functions --- src/Avalonia.Native.OSX/common.h | 4 ++++ src/Avalonia.Native.OSX/main.mm | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/Avalonia.Native.OSX/common.h b/src/Avalonia.Native.OSX/common.h index 303298ba02..4ff72b8820 100644 --- a/src/Avalonia.Native.OSX/common.h +++ b/src/Avalonia.Native.OSX/common.h @@ -10,4 +10,8 @@ extern IAvnPlatformThreadingInterface* CreatePlatformThreading(); extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events); extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events); + +extern NSPoint ToNSPoint (AvnPoint p); +extern AvnPoint ToAvnPoint (NSPoint p); +extern AvnPoint ConvertPointY (AvnPoint p); #endif diff --git a/src/Avalonia.Native.OSX/main.mm b/src/Avalonia.Native.OSX/main.mm index a04ed11a1e..0aa6ac2f6d 100644 --- a/src/Avalonia.Native.OSX/main.mm +++ b/src/Avalonia.Native.OSX/main.mm @@ -100,3 +100,33 @@ extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative() { return new AvaloniaNative(); }; + + +NSPoint ToNSPoint (AvnPoint p) +{ + NSPoint result; + result.x = p.X; + result.y = p.Y; + + return result; +} + +AvnPoint ToAvnPoint (NSPoint p) +{ + AvnPoint result; + result.X = p.x; + result.Y = p.y; + + return result; +} + + +AvnPoint ConvertPointY (AvnPoint p) +{ + auto sw = [NSScreen.screens objectAtIndex:0].frame; + + auto t = MAX(sw.origin.y, sw.origin.y + sw.size.height); + p.Y = t - p.Y; + + return p; +} From dc0f1b9db367a7c55364b6d19fbfa695352c23da Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 16 Sep 2018 17:34:19 +0100 Subject: [PATCH 04/17] working popup positioning --- src/Avalonia.Native.OSX/window.mm | 53 +++++++++++++++++++-------- src/Avalonia.Native/WindowImplBase.cs | 39 ++++++++++---------- src/headers/avalonia-native.h | 4 +- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index a35d6ba8df..2bc596d347 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -5,6 +5,7 @@ class WindowBaseImpl; @interface AvnView : NSView -(AvnView*) initWithParent: (WindowBaseImpl*) parent; -(NSEvent*) lastMouseDownEvent; +-(AvnPoint)translateLocalPoint:(AvnPoint)pt; @end @interface AvnWindow : NSWindow @@ -84,35 +85,56 @@ public: } - virtual AvnPoint GetPosition () + virtual HRESULT GetPosition (AvnPoint* ret) { + if(ret == nullptr) + { + return E_POINTER; + } + auto frame = [Window frame]; AvnPoint bottomLeft; bottomLeft.X = frame.origin.x; - bottomLeft.Y = frame.origin.y + frame.size.width; - - NSRect screen = [NSScreen.screens objectAtIndex:0].frame; - - auto t = MAX(screen.origin.y, screen.origin.y + screen.size.height); + bottomLeft.Y = frame.origin.y + frame.size.height; - bottomLeft.Y = t - bottomLeft.Y; + *ret = ConvertPointY(bottomLeft); - return bottomLeft; + return S_OK; } virtual void SetPosition (AvnPoint point) { - NSPoint nspoint; - nspoint.x = point.X; - nspoint.y = point.Y; + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + } + + virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) + { + if(ret == nullptr) + { + return E_POINTER; + } - NSRect screen = [NSScreen.screens objectAtIndex:0].frame; - auto t = MAX(screen.origin.y, screen.origin.y + screen.size.height); + point = ConvertPointY(point); + auto viewPoint = [Window convertScreenToBase:ToNSPoint(point)]; - nspoint.y = t - nspoint.y; + *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; - [Window setFrameTopLeftPoint:nspoint]; + 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 convertBaseToScreen:cocoaViewPoint]; + *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); + + return S_OK; } protected: @@ -235,7 +257,6 @@ protected: } } - auto timestamp = [event timestamp] * 1000; auto modifiers = [self getModifiers:[event modifierFlags]]; diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 00d8aba27d..bd5c03b583 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -170,6 +170,25 @@ namespace Avalonia.Native set => _native.SetPosition(value.ToAvnPoint()); } + public Point PointToClient(Point point) + { + return _native.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint(); + } + + public Point PointToScreen(Point point) + { + return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPoint(); + } + + public void Hide() + { + _native.Hide(); + } + + public void BeginMoveDrag() + { + _native.BeginMoveDrag(); + } #region Stubs public double Scaling => 1; @@ -201,30 +220,10 @@ namespace Avalonia.Native { } - public void Hide() - { - _native.Hide(); - } - - public void BeginMoveDrag() - { - _native.BeginMoveDrag(); - } - public void BeginResizeDrag(WindowEdge edge) { } - public Point PointToClient(Point point) - { - return point; - } - - public Point PointToScreen(Point point) - { - return point; - } - #endregion } } diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index d05f82045f..237ea62381 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -73,8 +73,10 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown virtual HRESULT Resize(double width, double height) = 0; virtual void Invalidate (AvnRect rect) = 0; virtual void BeginMoveDrag () = 0; - virtual AvnPoint GetPosition () = 0; + virtual HRESULT GetPosition (AvnPoint*ret) = 0; virtual void SetPosition (AvnPoint point) = 0; + virtual HRESULT PointToClient (AvnPoint point, AvnPoint*ret) = 0; + virtual HRESULT PointToScreen (AvnPoint point, AvnPoint*ret) = 0; }; AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase From ad66a0c67a8ef00b9485993e8514114f72a4bb03 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 18 Sep 2018 18:30:27 +0100 Subject: [PATCH 05/17] implement getScaling --- src/Avalonia.Native.OSX/window.mm | 15 +++++++++++++++ src/Avalonia.Native/WindowImplBase.cs | 2 +- src/headers/avalonia-native.h | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index 2bc596d347..929a28f4f4 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -61,6 +61,21 @@ public: 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}]; diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index bd5c03b583..e6d4af62e1 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -191,7 +191,7 @@ namespace Avalonia.Native } #region Stubs - public double Scaling => 1; + public double Scaling => _native.GetScaling(); public Action PositionChanged { get; set; } public Action Deactivated { get; set; } diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 237ea62381..27aa24536a 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -70,6 +70,7 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown virtual HRESULT Hide () = 0; virtual HRESULT Close() = 0; virtual HRESULT GetClientSize(AvnSize*ret) = 0; + virtual HRESULT GetScaling(double*ret)=0; virtual HRESULT Resize(double width, double height) = 0; virtual void Invalidate (AvnRect rect) = 0; virtual void BeginMoveDrag () = 0; From 3e6007dec90a30e36ee2402718d50c956d517e18 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 16:56:42 +0100 Subject: [PATCH 06/17] fix window positioning when first shown. --- src/Avalonia.Native.OSX/window.mm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index 929a28f4f4..7a201fb836 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -19,11 +19,16 @@ 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]; @@ -31,6 +36,7 @@ public: virtual HRESULT Show() { + SetPosition(lastPositionSet); UpdateStyle(); [Window makeKeyAndOrderFront:Window]; return S_OK; @@ -120,6 +126,7 @@ public: virtual void SetPosition (AvnPoint point) { + lastPositionSet = point; [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; } From 4208ca4f58f9ab11b8a24ac9c3582da8b4e1fd91 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 17:07:30 +0100 Subject: [PATCH 07/17] fix depricated warnings --- src/Avalonia.Native.OSX/window.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index 7a201fb836..a1d81f7a80 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -138,7 +138,7 @@ public: } point = ConvertPointY(point); - auto viewPoint = [Window convertScreenToBase:ToNSPoint(point)]; + auto viewPoint = [Window convertPointFromScreen:ToNSPoint(point)]; *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; @@ -153,7 +153,7 @@ public: } auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); - auto cocoaScreenPoint = [Window convertBaseToScreen:cocoaViewPoint]; + auto cocoaScreenPoint = [Window convertPointToScreen:cocoaViewPoint]; *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); return S_OK; From 4d3fcd9cbf184f87dfdbea9af8b658a2c063752e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 17:07:38 +0100 Subject: [PATCH 08/17] rename var --- src/Avalonia.Native.OSX/window.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index a1d81f7a80..beffc18f36 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -115,11 +115,11 @@ public: auto frame = [Window frame]; - AvnPoint bottomLeft; - bottomLeft.X = frame.origin.x; - bottomLeft.Y = frame.origin.y + frame.size.height; + AvnPoint topLeft; + topLeft.X = frame.origin.x; + topLeft.Y = frame.origin.y + frame.size.height; - *ret = ConvertPointY(bottomLeft); + *ret = ConvertPointY(topLeft); return S_OK; } From 6880b44b764c23eda36bfe4d06258b47015d5a4a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 17:13:57 +0100 Subject: [PATCH 09/17] fix some memory leaks. --- src/Avalonia.Native.OSX/main.mm | 41 +++++++++++++++++++------------ src/Avalonia.Native.OSX/window.mm | 7 +++--- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Native.OSX/main.mm b/src/Avalonia.Native.OSX/main.mm index 0aa6ac2f6d..31c98edda8 100644 --- a/src/Avalonia.Native.OSX/main.mm +++ b/src/Avalonia.Native.OSX/main.mm @@ -104,29 +104,38 @@ extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative() NSPoint ToNSPoint (AvnPoint p) { - NSPoint result; - result.x = p.X; - result.y = p.Y; - - return result; + @autoreleasepool + { + NSPoint result; + result.x = p.X; + result.y = p.Y; + + return result; + } } AvnPoint ToAvnPoint (NSPoint p) { - AvnPoint result; - result.X = p.x; - result.Y = p.y; - - return result; + @autoreleasepool + { + AvnPoint result; + result.X = p.x; + result.Y = p.y; + + return result; + } } AvnPoint ConvertPointY (AvnPoint p) { - auto sw = [NSScreen.screens objectAtIndex:0].frame; - - auto t = MAX(sw.origin.y, sw.origin.y + sw.size.height); - p.Y = t - p.Y; - - return p; + @autoreleasepool + { + auto sw = [NSScreen.screens objectAtIndex:0].frame; + + auto t = MAX(sw.origin.y, sw.origin.y + sw.size.height); + p.Y = t - p.Y; + + return p; + } } diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index beffc18f36..46b4371972 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -115,11 +115,10 @@ public: auto frame = [Window frame]; - AvnPoint topLeft; - topLeft.X = frame.origin.x; - topLeft.Y = frame.origin.y + frame.size.height; + ret->X = frame.origin.x; + ret->Y = frame.origin.y + frame.size.height; - *ret = ConvertPointY(topLeft); + *ret = ConvertPointY(*ret); return S_OK; } From 196ef4dbd4dcbf33d1d6d182eeae89c375059161 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 17:16:15 +0100 Subject: [PATCH 10/17] whitespace --- src/Avalonia.Native.OSX/window.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index 46b4371972..2b440fceac 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -244,7 +244,7 @@ protected: free(ptr); } -- (AvnPoint)translateLocalPoint:(AvnPoint)pt +- (AvnPoint) translateLocalPoint:(AvnPoint)pt { pt.Y = [self bounds].size.height - pt.Y; return pt; From 62ce672458dad558c2669821b187ec1fcec0e7f6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 17:17:01 +0100 Subject: [PATCH 11/17] whitespace --- src/Avalonia.Native.OSX/window.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index 2b440fceac..ce5923089c 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -3,9 +3,9 @@ class WindowBaseImpl; @interface AvnView : NSView --(AvnView*) initWithParent: (WindowBaseImpl*) parent; --(NSEvent*) lastMouseDownEvent; --(AvnPoint)translateLocalPoint:(AvnPoint)pt; +-(AvnView*) initWithParent: (WindowBaseImpl*) parent; +-(NSEvent*) lastMouseDownEvent; +-(AvnPoint) translateLocalPoint:(AvnPoint)pt; @end @interface AvnWindow : NSWindow From 7b4275b2d5d93be742cc59a5ed7d9912da10586d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 19:14:42 +0100 Subject: [PATCH 12/17] add interfaces required for system dialogs --- src/Avalonia.Native/AvaloniaNativePlatform.cs | 1 + src/Avalonia.Native/SystemDialogs.cs | 85 +++++++++++++++++++ src/headers/avalonia-native.h | 28 ++++++ 3 files changed, 114 insertions(+) create mode 100644 src/Avalonia.Native/SystemDialogs.cs diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 515f56167f..7f43256ddc 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -74,6 +74,7 @@ namespace Avalonia.Native .Bind().ToSingleton() .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) .Bind().ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface())); } diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs new file mode 100644 index 0000000000..59d2731248 --- /dev/null +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -0,0 +1,85 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Native.Interop; +using Avalonia.Platform; + +namespace Avalonia.Native +{ + public class SystemDialogs : ISystemDialogImpl + { + IAvnSystemDialogs _native; + + public SystemDialogs(IAvnSystemDialogs native) + { + _native = native; + } + + public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) + { + 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))); + } + + return events.Task; + } + + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + { + var events = new SystemDialogEvents(); + + _native.SelectFolderDialog(events, dialog.Title, dialog.InitialDirectory); + + return (await events.Task).FirstOrDefault(); + } + } + + public class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents + { + private TaskCompletionSource _tcs; + + public SystemDialogEvents() + { + _tcs = new TaskCompletionSource(); + } + + public Task Task => _tcs.Task; + + public void OnCompleted(int numResults, IntPtr trFirstResultRef) + { + string[] results = new string[numResults]; + + unsafe + { + var ptr = (IntPtr*)trFirstResultRef.ToPointer(); + + for (int i = 0; i < numResults; i++) + { + results[i] = Marshal.PtrToStringAnsi(*ptr); + + ptr++; + } + } + + _tcs.SetResult(results); + } + } +} diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 27aa24536a..82404c1b8d 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -7,6 +7,8 @@ struct IAvnWindow; struct IAvnPopup; struct IAvnMacOptions; struct IAvnPlatformThreadingInterface; +struct IAvnSystemDialogEvents; +struct IAvnSystemDialogs; struct AvnSize { @@ -62,6 +64,7 @@ public: virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) = 0; virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnPopup** ppv) = 0; virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0; + virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0; }; AVNCOM(IAvnWindowBase, 02) : virtual IUnknown @@ -143,4 +146,29 @@ AVNCOM(IAvnPlatformThreadingInterface, 0b) : virtual IUnknown virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) = 0; }; +AVNCOM(IAvnSystemDialogEvents, 0c) : virtual IUnknown +{ + virtual void OnCompleted (int numResults, void* ptrFirstResult) = 0; +}; + +AVNCOM(IAvnSystemDialogs, 0d) : virtual IUnknown +{ + virtual void SelectFolderDialog (IAvnSystemDialogEvents* events, + const char* title, + const char* initialPath) = 0; + + virtual void OpenFileDialog (IAvnSystemDialogEvents* events, + bool allowMultiple, + const char* title, + const char* initialDirectory, + const char* intialFile, + const char* filters) = 0; + + virtual void SaveFileDialog (IAvnSystemDialogEvents* events, + const char* title, + const char* initialDirectory, + const char* intialFile, + const char* filters) = 0; +}; + extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative(); From 9cb9c1fef11d1d25a9e1eff9b671b71b64a04469 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 19:25:04 +0100 Subject: [PATCH 13/17] working dialog callbacks --- .../project.pbxproj | 4 ++ src/Avalonia.Native.OSX/SystemDialogs.mm | 36 +++++++++++++++ src/Avalonia.Native.OSX/common.h | 1 + src/Avalonia.Native.OSX/main.mm | 6 +++ src/Avalonia.Native/SystemDialogs.cs | 46 ++++++++++--------- 5 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 src/Avalonia.Native.OSX/SystemDialogs.mm 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 8338f55bde..2de6fac5ac 100644 --- a/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; }; AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; }; AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; }; @@ -15,6 +16,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 = ""; }; 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 = ""; }; @@ -51,6 +53,7 @@ AB661C212148288600291242 /* common.h */, AB661C1F2148286E00291242 /* window.mm */, AB00E4F62147CA920032A60A /* main.mm */, + 37C09D8721580FE4006A6758 /* SystemDialogs.mm */, AB7A61F02147C815003C5833 /* Products */, AB661C1C2148230E00291242 /* Frameworks */, ); @@ -133,6 +136,7 @@ files = ( AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */, AB00E4F72147CA920032A60A /* main.mm in Sources */, + 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, AB661C202148286E00291242 /* window.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/src/Avalonia.Native.OSX/SystemDialogs.mm b/src/Avalonia.Native.OSX/SystemDialogs.mm new file mode 100644 index 0000000000..7d77e4fa22 --- /dev/null +++ b/src/Avalonia.Native.OSX/SystemDialogs.mm @@ -0,0 +1,36 @@ +#include "common.h" + +class SystemDialogs : public ComSingleObject +{ + virtual void SelectFolderDialog (IAvnSystemDialogEvents* events, + const char* title, + const char* initialPath) + { + + } + + virtual void OpenFileDialog (IAvnSystemDialogEvents* events, + bool allowMultiple, + const char* title, + const char* initialDirectory, + const char* intialFile, + const char* filters) + { + events->OnCompleted(0, nullptr); + } + + virtual void SaveFileDialog (IAvnSystemDialogEvents* events, + const char* title, + const char* initialDirectory, + const char* intialFile, + const char* filters) + { + + } + +}; + +extern IAvnSystemDialogs* CreateSystemDialogs() +{ + return new SystemDialogs(); +} diff --git a/src/Avalonia.Native.OSX/common.h b/src/Avalonia.Native.OSX/common.h index 4ff72b8820..6b1a45969e 100644 --- a/src/Avalonia.Native.OSX/common.h +++ b/src/Avalonia.Native.OSX/common.h @@ -10,6 +10,7 @@ extern IAvnPlatformThreadingInterface* CreatePlatformThreading(); extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events); extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events); +extern IAvnSystemDialogs* CreateSystemDialogs(); extern NSPoint ToNSPoint (AvnPoint p); extern AvnPoint ToAvnPoint (NSPoint p); diff --git a/src/Avalonia.Native.OSX/main.mm b/src/Avalonia.Native.OSX/main.mm index 31c98edda8..23fc26d28e 100644 --- a/src/Avalonia.Native.OSX/main.mm +++ b/src/Avalonia.Native.OSX/main.mm @@ -94,6 +94,12 @@ public: *ppv = CreatePlatformThreading(); return S_OK; } + + virtual HRESULT CreateSystemDialogs(IAvnSystemDialogs** ppv) + { + *ppv = ::CreateSystemDialogs(); + return S_OK; + } }; extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative() diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs index 59d2731248..8a9464e79f 100644 --- a/src/Avalonia.Native/SystemDialogs.cs +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -20,35 +20,37 @@ namespace Avalonia.Native public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) { - 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 + using (var events = new SystemDialogEvents()) { - _native.SaveFileDialog(events, - dialog.Title, - dialog.InitialDirectory, - dialog.InitialFileName, - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); - } + 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))); + } - return events.Task; + return events.Task; + } } public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) { - var events = new SystemDialogEvents(); - - _native.SelectFolderDialog(events, dialog.Title, dialog.InitialDirectory); + using (var events = new SystemDialogEvents()) + { + _native.SelectFolderDialog(events, dialog.Title, dialog.InitialDirectory); - return (await events.Task).FirstOrDefault(); + return (await events.Task).FirstOrDefault(); + } } } From 668a4aca9ea5f67239d3c8abc6b084f4828043e7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 20:30:30 +0100 Subject: [PATCH 14/17] work towards dialogs with parent window handle --- .../project.pbxproj | 2 + src/Avalonia.Native.OSX/SystemDialogs.mm | 39 +++- src/Avalonia.Native.OSX/window.h | 182 ++++++++++++++++++ src/Avalonia.Native.OSX/window.mm | 171 +--------------- src/Avalonia.Native/SystemDialogs.cs | 50 ++--- src/Avalonia.Native/WindowImpl.cs | 2 + src/headers/avalonia-native.h | 13 +- 7 files changed, 255 insertions(+), 204 deletions(-) create mode 100644 src/Avalonia.Native.OSX/window.h 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, From 93e0782e640f50450cbf8e616b49ac02def4d485 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 20:35:04 +0100 Subject: [PATCH 15/17] working modal system dialog. --- src/Avalonia.Native.OSX/SystemDialogs.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native.OSX/SystemDialogs.mm b/src/Avalonia.Native.OSX/SystemDialogs.mm index bb5fc60077..66f1cf4419 100644 --- a/src/Avalonia.Native.OSX/SystemDialogs.mm +++ b/src/Avalonia.Native.OSX/SystemDialogs.mm @@ -27,7 +27,7 @@ class SystemDialogs : public ComSingleObject(parentWindowHandle); - [panel beginSheet:windowBase->Window completionHandler:^(NSModalResponse result) { + [panel beginSheetModalForWindow:windowBase->Window completionHandler:^(NSModalResponse result) { if(result == NSFileHandlingPanelOKButton) { From c38cdf641c280bdc3184355153fe897b5bb36fac Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 21:08:57 +0100 Subject: [PATCH 16/17] working open file and open folder dialogs. --- src/Avalonia.Native.OSX/SystemDialogs.mm | 158 ++++++++++++++++++++--- src/headers/avalonia-native.h | 4 +- 2 files changed, 141 insertions(+), 21 deletions(-) diff --git a/src/Avalonia.Native.OSX/SystemDialogs.mm b/src/Avalonia.Native.OSX/SystemDialogs.mm index 66f1cf4419..ff5c1bcd47 100644 --- a/src/Avalonia.Native.OSX/SystemDialogs.mm +++ b/src/Avalonia.Native.OSX/SystemDialogs.mm @@ -6,9 +6,75 @@ class SystemDialogs : public ComSingleObject 0) + { + void* strings[urls.count]; + + for(int i = 0; i < urls.count; i++) + { + auto url = [urls objectAtIndex:i]; + + auto string = [url absoluteString]; + string = [string substringFromIndex:7]; + + strings[i] = (void*)[string UTF8String]; + } + + events->OnCompleted((int)urls.count, &strings[0]); + + [panel orderOut:panel]; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(parentWindowHandle); + [windowBase->Window makeKeyAndOrderFront:windowBase->Window]; + } + + return; + } + } + + events->OnCompleted(0, nullptr); + + }; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(parentWindowHandle); + + [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; + } + else + { + [panel beginWithCompletionHandler: handler]; + } + } } virtual void OpenFileDialog (IAvnWindow* parentWindowHandle, @@ -16,36 +82,90 @@ class SystemDialogs : public ComSingleObject(parentWindowHandle); + auto panel = [NSOpenPanel openPanel]; - [panel beginSheetModalForWindow:windowBase->Window completionHandler:^(NSModalResponse result) { - if(result == NSFileHandlingPanelOKButton) + panel.allowsMultipleSelection = allowMultiple; + + if(title != nullptr) + { + panel.title = [NSString stringWithUTF8String:title]; + } + + if(initialDirectory != nullptr) + { + auto directoryString = [NSString stringWithUTF8String:initialDirectory]; + panel.directoryURL = [NSURL fileURLWithPath:directoryString]; + } + + if(initialFile != nullptr) + { + panel.nameFieldStringValue = [NSString stringWithUTF8String:initialFile]; + } + + if(filters != nullptr) + { + auto filtersString = [NSString stringWithUTF8String:filters]; + + if(filtersString.length > 0) { + auto allowedTypes = [filtersString componentsSeparatedByString:@";"]; + panel.allowedFileTypes = allowedTypes; } - - events->OnCompleted(0, nullptr); - }]; - } - else - { - [panel beginWithCompletionHandler:^(NSModalResponse result) { + } + + auto handler = ^(NSModalResponse result) { if(result == NSFileHandlingPanelOKButton) { + auto urls = [panel URLs]; + if(urls.count > 0) + { + void* strings[urls.count]; + + for(int i = 0; i < urls.count; i++) + { + auto url = [urls objectAtIndex:i]; + + auto string = [url absoluteString]; + string = [string substringFromIndex:7]; + + strings[i] = (void*)[string UTF8String]; + } + + events->OnCompleted((int)urls.count, &strings[0]); + + [panel orderOut:panel]; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(parentWindowHandle); + [windowBase->Window makeKeyAndOrderFront:windowBase->Window]; + } + + return; + } } events->OnCompleted(0, nullptr); - }]; + + }; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(parentWindowHandle); + + [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; + } + else + { + [panel beginWithCompletionHandler: handler]; + } } } diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 92997c1d82..4775859395 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -163,14 +163,14 @@ AVNCOM(IAvnSystemDialogs, 0d) : virtual IUnknown bool allowMultiple, const char* title, const char* initialDirectory, - const char* intialFile, + const char* initialFile, const char* filters) = 0; virtual void SaveFileDialog (IAvnWindow* parentWindowHandle, IAvnSystemDialogEvents* events, const char* title, const char* initialDirectory, - const char* intialFile, + const char* initialFile, const char* filters) = 0; }; From 35a4e1149e93a11e687e5c5d785b9eb0f02ba3a0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Sep 2018 21:13:46 +0100 Subject: [PATCH 17/17] implemented save file dialog --- src/Avalonia.Native.OSX/SystemDialogs.mm | 74 +++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Native.OSX/SystemDialogs.mm b/src/Avalonia.Native.OSX/SystemDialogs.mm index ff5c1bcd47..3f508ca8f7 100644 --- a/src/Avalonia.Native.OSX/SystemDialogs.mm +++ b/src/Avalonia.Native.OSX/SystemDialogs.mm @@ -173,10 +173,80 @@ class SystemDialogs : public ComSingleObject 0) + { + auto allowedTypes = [filtersString componentsSeparatedByString:@";"]; + + panel.allowedFileTypes = allowedTypes; + } + } + + auto handler = ^(NSModalResponse result) { + if(result == NSFileHandlingPanelOKButton) + { + void* strings[1]; + + auto url = [panel URL]; + + auto string = [url absoluteString]; + string = [string substringFromIndex:7]; + strings[0] = (void*)[string UTF8String]; + + events->OnCompleted(1, &strings[0]); + + [panel orderOut:panel]; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(parentWindowHandle); + [windowBase->Window makeKeyAndOrderFront:windowBase->Window]; + } + + return; + } + + events->OnCompleted(0, nullptr); + + }; + + if(parentWindowHandle != nullptr) + { + auto windowBase = dynamic_cast(parentWindowHandle); + + [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; + } + else + { + [panel beginWithCompletionHandler: handler]; + } + } } };