14 changed files with 760 additions and 124 deletions
@ -0,0 +1,257 @@ |
|||
#include "common.h" |
|||
#include "window.h" |
|||
|
|||
class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemDialogs> |
|||
{ |
|||
virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle, |
|||
IAvnSystemDialogEvents* events, |
|||
const char* title, |
|||
const char* initialDirectory) |
|||
{ |
|||
@autoreleasepool |
|||
{ |
|||
auto panel = [NSOpenPanel openPanel]; |
|||
|
|||
panel.canChooseDirectories = true; |
|||
panel.canCreateDirectories = true; |
|||
panel.canChooseFiles = false; |
|||
|
|||
if(title != nullptr) |
|||
{ |
|||
panel.title = [NSString stringWithUTF8String:title]; |
|||
} |
|||
|
|||
if(initialDirectory != nullptr) |
|||
{ |
|||
auto directoryString = [NSString stringWithUTF8String:initialDirectory]; |
|||
panel.directoryURL = [NSURL fileURLWithPath:directoryString]; |
|||
} |
|||
|
|||
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<WindowBaseImpl*>(parentWindowHandle); |
|||
[windowBase->Window makeKeyAndOrderFront:windowBase->Window]; |
|||
} |
|||
|
|||
return; |
|||
} |
|||
} |
|||
|
|||
events->OnCompleted(0, nullptr); |
|||
|
|||
}; |
|||
|
|||
if(parentWindowHandle != nullptr) |
|||
{ |
|||
auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle); |
|||
|
|||
[panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; |
|||
} |
|||
else |
|||
{ |
|||
[panel beginWithCompletionHandler: handler]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
virtual void OpenFileDialog (IAvnWindow* parentWindowHandle, |
|||
IAvnSystemDialogEvents* events, |
|||
bool allowMultiple, |
|||
const char* title, |
|||
const char* initialDirectory, |
|||
const char* initialFile, |
|||
const char* filters) |
|||
{ |
|||
@autoreleasepool |
|||
{ |
|||
auto panel = [NSOpenPanel openPanel]; |
|||
|
|||
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; |
|||
} |
|||
} |
|||
|
|||
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<WindowBaseImpl*>(parentWindowHandle); |
|||
[windowBase->Window makeKeyAndOrderFront:windowBase->Window]; |
|||
} |
|||
|
|||
return; |
|||
} |
|||
} |
|||
|
|||
events->OnCompleted(0, nullptr); |
|||
|
|||
}; |
|||
|
|||
if(parentWindowHandle != nullptr) |
|||
{ |
|||
auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle); |
|||
|
|||
[panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; |
|||
} |
|||
else |
|||
{ |
|||
[panel beginWithCompletionHandler: handler]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
virtual void SaveFileDialog (IAvnWindow* parentWindowHandle, |
|||
IAvnSystemDialogEvents* events, |
|||
const char* title, |
|||
const char* initialDirectory, |
|||
const char* initialFile, |
|||
const char* filters) |
|||
{ |
|||
@autoreleasepool |
|||
{ |
|||
auto panel = [NSSavePanel savePanel]; |
|||
|
|||
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; |
|||
} |
|||
} |
|||
|
|||
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<WindowBaseImpl*>(parentWindowHandle); |
|||
[windowBase->Window makeKeyAndOrderFront:windowBase->Window]; |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
events->OnCompleted(0, nullptr); |
|||
|
|||
}; |
|||
|
|||
if(parentWindowHandle != nullptr) |
|||
{ |
|||
auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle); |
|||
|
|||
[panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; |
|||
} |
|||
else |
|||
{ |
|||
[panel beginWithCompletionHandler: handler]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
}; |
|||
|
|||
extern IAvnSystemDialogs* CreateSystemDialogs() |
|||
{ |
|||
return new SystemDialogs(); |
|||
} |
|||
@ -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 <NSWindowDelegate> |
|||
-(AvnWindow*) initWithParent: (WindowBaseImpl*) parent; |
|||
-(void) setCanBecomeKeyAndMain; |
|||
@end |
|||
|
|||
class WindowBaseImpl : public ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase> |
|||
{ |
|||
public: |
|||
AvnView* View; |
|||
AvnWindow* Window; |
|||
ComPtr<IAvnWindowBaseEvents> 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 */ |
|||
@ -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 }; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
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<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) |
|||
{ |
|||
var events = new SystemDialogEvents(); |
|||
|
|||
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 Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) |
|||
{ |
|||
var events = new SystemDialogEvents(); |
|||
|
|||
_native.SelectFolderDialog((parent as WindowImpl).Native, events, dialog.Title, dialog.InitialDirectory); |
|||
|
|||
return events.Task.ContinueWith(t => { events.Dispose(); return t.Result.FirstOrDefault(); }); |
|||
} |
|||
} |
|||
|
|||
public class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents |
|||
{ |
|||
private TaskCompletionSource<string[]> _tcs; |
|||
|
|||
public SystemDialogEvents() |
|||
{ |
|||
_tcs = new TaskCompletionSource<string[]>(); |
|||
} |
|||
|
|||
public Task<string[]> 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); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue