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