committed by
GitHub
31 changed files with 155 additions and 2253 deletions
@ -0,0 +1,89 @@ |
|||||
|
jobs: |
||||
|
- job: Linux |
||||
|
pool: |
||||
|
vmImage: 'ubuntu-16.04' |
||||
|
steps: |
||||
|
- task: CmdLine@2 |
||||
|
inputs: |
||||
|
script: | |
||||
|
sudo apt-get update |
||||
|
sudo apt-get install castxml |
||||
|
- task: CmdLine@2 |
||||
|
inputs: |
||||
|
script: | |
||||
|
dotnet tool install -g Cake.Tool --version 0.30.0 |
||||
|
|
||||
|
- script: | |
||||
|
export PATH="$PATH:$HOME/.dotnet/tools" |
||||
|
dotnet --info |
||||
|
printenv |
||||
|
dotnet cake build.cake -target="Azure-Linux" -configuration="Release" |
||||
|
|
||||
|
- job: macOS |
||||
|
pool: |
||||
|
vmImage: 'xcode9-macos10.13' |
||||
|
steps: |
||||
|
- task: DotNetCoreInstaller@0 |
||||
|
inputs: |
||||
|
version: '2.1.403' |
||||
|
- task: Xcode@5 |
||||
|
inputs: |
||||
|
actions: 'build' |
||||
|
scheme: '' |
||||
|
sdk: 'macosx10.13' |
||||
|
configuration: 'Release' |
||||
|
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' |
||||
|
xcodeVersion: 'default' # Options: 8, 9, default, specifyPath |
||||
|
args: '-derivedDataPath ./' |
||||
|
- task: CmdLine@2 |
||||
|
inputs: |
||||
|
script: brew install castxml |
||||
|
- task: CmdLine@2 |
||||
|
inputs: |
||||
|
script: | |
||||
|
dotnet tool install -g Cake.Tool --version 0.30.0 |
||||
|
- script: | |
||||
|
export COREHOST_TRACE=0 |
||||
|
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 |
||||
|
export DOTNET_CLI_TELEMETRY_OPTOUT=1 |
||||
|
which dotnet |
||||
|
dotnet --info |
||||
|
export PATH="$PATH:$HOME/.dotnet/tools" |
||||
|
dotnet --info |
||||
|
printenv |
||||
|
dotnet cake build.cake -target="Azure-OSX" -configuration="Release" |
||||
|
|
||||
|
- task: PublishBuildArtifacts@1 |
||||
|
inputs: |
||||
|
pathToPublish: '$(Build.SourcesDirectory)/Build/Products/Release/' |
||||
|
artifactName: 'Avalonia.Native.OSX' |
||||
|
- task: PublishBuildArtifacts@1 |
||||
|
inputs: |
||||
|
pathToPublish: '$(Build.SourcesDirectory)/artifacts/bin' |
||||
|
artifactName: 'BinariesOSX' |
||||
|
|
||||
|
- job: Windows |
||||
|
pool: |
||||
|
vmImage: 'vs2017-win2016' |
||||
|
steps: |
||||
|
- task: CmdLine@2 |
||||
|
inputs: |
||||
|
script: | |
||||
|
dotnet tool install -g Cake.Tool --version 0.30.0 |
||||
|
- task: CmdLine@2 |
||||
|
inputs: |
||||
|
script: | |
||||
|
set PATH=%PATH%;%USERPROFILE%\.dotnet\tools |
||||
|
dotnet cake build.cake -target="Azure-Windows" -configuration="Release" |
||||
|
- task: PublishBuildArtifacts@1 |
||||
|
inputs: |
||||
|
pathtoPublish: '$(Build.SourcesDirectory)/artifacts/nuget' |
||||
|
artifactName: 'NuGet' |
||||
|
- task: PublishBuildArtifacts@1 |
||||
|
inputs: |
||||
|
pathToPublish: '$(Build.SourcesDirectory)/artifacts/zip' |
||||
|
artifactName: 'Samples' |
||||
|
- task: PublishBuildArtifacts@1 |
||||
|
inputs: |
||||
|
pathToPublish: '$(Build.SourcesDirectory)/artifacts/bin' |
||||
|
artifactName: 'BinariesWindows' |
||||
@ -1,5 +0,0 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="MonoMac.NetStandard" Version="0.0.4" /> |
|
||||
</ItemGroup> |
|
||||
</Project> |
|
||||
@ -1,6 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Text;using Avalonia.MonoMac; |
|
||||
using Avalonia.Platform; |
|
||||
[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 1, "MonoMac", typeof(MonoMacPlatform), nameof(MonoMacPlatform.Initialize))] |
|
||||
|
|
||||
@ -1,20 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
<PropertyGroup> |
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks> |
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> |
|
||||
</PropertyGroup> |
|
||||
<ItemGroup> |
|
||||
<Compile Include="..\..\Shared\WindowResizeDragHelper.cs" Link="WindowResizeDragHelper.cs" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" /> |
|
||||
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" /> |
|
||||
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" /> |
|
||||
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" /> |
|
||||
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> |
|
||||
<ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj" /> |
|
||||
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" /> |
|
||||
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" /> |
|
||||
</ItemGroup> |
|
||||
<Import Project="..\..\..\build\MonoMac.props" /> |
|
||||
</Project> |
|
||||
@ -1,29 +0,0 @@ |
|||||
using System.Threading.Tasks; |
|
||||
using Avalonia.Input.Platform; |
|
||||
using MonoMac.AppKit; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class ClipboardImpl : IClipboard |
|
||||
{ |
|
||||
public Task<string> GetTextAsync() |
|
||||
{ |
|
||||
return Task.FromResult(NSPasteboard.GeneralPasteboard.GetStringForType(NSPasteboard.NSStringType)); |
|
||||
} |
|
||||
|
|
||||
public Task SetTextAsync(string text) |
|
||||
{ |
|
||||
NSPasteboard.GeneralPasteboard.ClearContents(); |
|
||||
if (text != null) |
|
||||
NSPasteboard.GeneralPasteboard.SetStringForType(text, NSPasteboard.NSStringType); |
|
||||
return Task.CompletedTask; |
|
||||
} |
|
||||
|
|
||||
public Task ClearAsync() |
|
||||
{ |
|
||||
NSPasteboard.GeneralPasteboard.ClearContents(); |
|
||||
return Task.CompletedTask; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@ -1,66 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using Avalonia.Input; |
|
||||
using Avalonia.Platform; |
|
||||
using MonoMac.AppKit; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class Cursor : IPlatformHandle |
|
||||
{ |
|
||||
|
|
||||
public NSCursor Native { get; } |
|
||||
|
|
||||
public IntPtr Handle => Native.Handle; |
|
||||
|
|
||||
public string HandleDescriptor => "NSCursor"; |
|
||||
|
|
||||
public Cursor(NSCursor native) |
|
||||
{ |
|
||||
Native = native; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class CursorFactoryStub : IStandardCursorFactory |
|
||||
{ |
|
||||
Dictionary<StandardCursorType, NSCursor> _cache; |
|
||||
|
|
||||
public CursorFactoryStub() |
|
||||
{ |
|
||||
//TODO: Load diagonal cursors from webkit
|
|
||||
//See https://stackoverflow.com/q/10733228
|
|
||||
_cache = new Dictionary<StandardCursorType, NSCursor>() |
|
||||
{ |
|
||||
[StandardCursorType.Arrow] = NSCursor.ArrowCursor, |
|
||||
[StandardCursorType.AppStarting] = NSCursor.ArrowCursor, //TODO
|
|
||||
[StandardCursorType.BottomLeftCorner] = NSCursor.CrosshairCursor, //TODO
|
|
||||
[StandardCursorType.BottomRightCorner] = NSCursor.CrosshairCursor, //TODO
|
|
||||
[StandardCursorType.BottomSize] = NSCursor.ResizeDownCursor, |
|
||||
[StandardCursorType.Cross] = NSCursor.CrosshairCursor, |
|
||||
[StandardCursorType.Hand] = NSCursor.PointingHandCursor, |
|
||||
[StandardCursorType.Help] = NSCursor.ContextualMenuCursor, |
|
||||
[StandardCursorType.Ibeam] = NSCursor.IBeamCursor, |
|
||||
[StandardCursorType.LeftSide] = NSCursor.ResizeLeftCursor, |
|
||||
[StandardCursorType.No] = NSCursor.OperationNotAllowedCursor, |
|
||||
[StandardCursorType.RightSide] = NSCursor.ResizeRightCursor, |
|
||||
[StandardCursorType.SizeAll] = NSCursor.CrosshairCursor, //TODO
|
|
||||
[StandardCursorType.SizeNorthSouth] = NSCursor.ResizeUpDownCursor, |
|
||||
[StandardCursorType.SizeWestEast] = NSCursor.ResizeLeftRightCursor, |
|
||||
[StandardCursorType.TopLeftCorner] = NSCursor.CrosshairCursor, //TODO
|
|
||||
[StandardCursorType.TopRightCorner] = NSCursor.CrosshairCursor, //TODO
|
|
||||
[StandardCursorType.TopSide] = NSCursor.ResizeUpCursor, |
|
||||
[StandardCursorType.UpArrow] = NSCursor.ResizeUpCursor, |
|
||||
[StandardCursorType.Wait] = NSCursor.ArrowCursor, //TODO
|
|
||||
[StandardCursorType.DragMove] = NSCursor.DragCopyCursor, // TODO
|
|
||||
[StandardCursorType.DragCopy] = NSCursor.DragCopyCursor, |
|
||||
[StandardCursorType.DragLink] = NSCursor.DragLinkCursor, |
|
||||
|
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
public IPlatformHandle GetCursor(StandardCursorType cursorType) |
|
||||
{ |
|
||||
return new Cursor(_cache[cursorType]); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,119 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.IO; |
|
||||
using System.Linq; |
|
||||
using System.Reactive.Linq; |
|
||||
using System.Reactive.Subjects; |
|
||||
using System.Runtime.Serialization.Formatters.Binary; |
|
||||
using System.Threading.Tasks; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Input; |
|
||||
using Avalonia.Input.Platform; |
|
||||
using Avalonia.Input.Raw; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.CoreGraphics; |
|
||||
using MonoMac.Foundation; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
public class DragSource : NSDraggingSource, IPlatformDragSource |
|
||||
{ |
|
||||
private const string NSPasteboardTypeString = "public.utf8-plain-text"; |
|
||||
private const string NSPasteboardTypeFileUrl = "public.file-url"; |
|
||||
|
|
||||
private readonly Subject<DragDropEffects> _result = new Subject<DragDropEffects>(); |
|
||||
private readonly IInputManager _inputManager; |
|
||||
private DragDropEffects _allowedEffects; |
|
||||
|
|
||||
public override bool IgnoreModifierKeysWhileDragging => false; |
|
||||
|
|
||||
public DragSource() |
|
||||
{ |
|
||||
_inputManager = AvaloniaLocator.Current.GetService<IInputManager>(); |
|
||||
} |
|
||||
|
|
||||
private string DataFormatToUTI(string s) |
|
||||
{ |
|
||||
if (s == DataFormats.FileNames) |
|
||||
return NSPasteboardTypeFileUrl; |
|
||||
if (s == DataFormats.Text) |
|
||||
return NSPasteboardTypeString; |
|
||||
return s; |
|
||||
} |
|
||||
|
|
||||
private NSDraggingItem CreateDraggingItem(string format, object data) |
|
||||
{ |
|
||||
var pasteboardItem = new NSPasteboardItem(); |
|
||||
NSData nsData; |
|
||||
if (data is string s) |
|
||||
{ |
|
||||
if (format == DataFormats.FileNames) |
|
||||
s = new Uri(s).AbsoluteUri; // Ensure file uris...
|
|
||||
nsData = NSData.FromString(s); |
|
||||
} |
|
||||
else if (data is Stream strm) |
|
||||
nsData = NSData.FromStream(strm); |
|
||||
else if (data is byte[] bytes) |
|
||||
nsData = NSData.FromArray(bytes); |
|
||||
else |
|
||||
{ |
|
||||
BinaryFormatter bf = new BinaryFormatter(); |
|
||||
using (var ms = new MemoryStream()) |
|
||||
{ |
|
||||
bf.Serialize(ms, data); |
|
||||
ms.Position = 0; |
|
||||
nsData = NSData.FromStream(ms); |
|
||||
} |
|
||||
} |
|
||||
pasteboardItem.SetDataForType(nsData, DataFormatToUTI(format)); |
|
||||
|
|
||||
NSPasteboardWriting writing = new NSPasteboardWriting(pasteboardItem.Handle); |
|
||||
|
|
||||
return new NSDraggingItem(writing); |
|
||||
} |
|
||||
|
|
||||
public IEnumerable<NSDraggingItem> CreateDraggingItems(string format, object data) |
|
||||
{ |
|
||||
if (format == DataFormats.FileNames && data is IEnumerable<string> files) |
|
||||
{ |
|
||||
foreach (var file in files) |
|
||||
yield return CreateDraggingItem(format, file); |
|
||||
|
|
||||
yield break; |
|
||||
} |
|
||||
|
|
||||
yield return CreateDraggingItem(format, data); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
public async Task<DragDropEffects> DoDragDrop(IDataObject data, DragDropEffects allowedEffects) |
|
||||
{ |
|
||||
// We need the TopLevelImpl + a mouse location so we just wait for the next event.
|
|
||||
var mouseEv = await _inputManager.PreProcess.OfType<RawMouseEventArgs>().FirstAsync(); |
|
||||
var view = ((mouseEv.Root as TopLevel)?.PlatformImpl as TopLevelImpl)?.View; |
|
||||
if (view == null) |
|
||||
return DragDropEffects.None; |
|
||||
|
|
||||
// Prepare the source event:
|
|
||||
var pt = view.TranslateLocalPoint(mouseEv.Position).ToMonoMacPoint(); |
|
||||
var ev = NSEvent.MouseEvent(NSEventType.LeftMouseDown, pt, 0, 0, 0, null, 0, 0, 0); |
|
||||
|
|
||||
_allowedEffects = allowedEffects; |
|
||||
var items = data.GetDataFormats().SelectMany(fmt => CreateDraggingItems(fmt, data.Get(fmt))).ToArray(); |
|
||||
view.BeginDraggingSession(items ,ev, this); |
|
||||
|
|
||||
return await _result; |
|
||||
} |
|
||||
|
|
||||
public override NSDragOperation DraggingSourceOperationMaskForLocal(bool flag) |
|
||||
{ |
|
||||
return DraggingInfo.ConvertDragOperation(_allowedEffects); |
|
||||
} |
|
||||
|
|
||||
public override void DraggedImageEndedAtOperation(NSImage image, CGPoint screenPoint, NSDragOperation operation) |
|
||||
{ |
|
||||
_result.OnNext(DraggingInfo.ConvertDragOperation(operation)); |
|
||||
_result.OnCompleted(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,89 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using Avalonia.Input; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.Foundation; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class DraggingInfo : IDataObject |
|
||||
{ |
|
||||
private readonly NSDraggingInfo _info; |
|
||||
|
|
||||
public DraggingInfo(NSDraggingInfo info) |
|
||||
{ |
|
||||
_info = info; |
|
||||
} |
|
||||
|
|
||||
internal static NSDragOperation ConvertDragOperation(DragDropEffects d) |
|
||||
{ |
|
||||
NSDragOperation result = NSDragOperation.None; |
|
||||
if (d.HasFlag(DragDropEffects.Copy)) |
|
||||
result |= NSDragOperation.Copy; |
|
||||
if (d.HasFlag(DragDropEffects.Link)) |
|
||||
result |= NSDragOperation.Link; |
|
||||
if (d.HasFlag(DragDropEffects.Move)) |
|
||||
result |= NSDragOperation.Move; |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
internal static DragDropEffects ConvertDragOperation(NSDragOperation d) |
|
||||
{ |
|
||||
DragDropEffects result = DragDropEffects.None; |
|
||||
if (d.HasFlag(NSDragOperation.Copy)) |
|
||||
result |= DragDropEffects.Copy; |
|
||||
if (d.HasFlag(NSDragOperation.Link)) |
|
||||
result |= DragDropEffects.Link; |
|
||||
if (d.HasFlag(NSDragOperation.Move)) |
|
||||
result |= DragDropEffects.Move; |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
public Point Location => new Point(_info.DraggingLocation.X, _info.DraggingLocation.Y); |
|
||||
|
|
||||
public IEnumerable<string> GetDataFormats() |
|
||||
{ |
|
||||
return _info.DraggingPasteboard.Types.Select(NSTypeToWellknownType); |
|
||||
} |
|
||||
|
|
||||
private string NSTypeToWellknownType(string type) |
|
||||
{ |
|
||||
if (type == NSPasteboard.NSStringType) |
|
||||
return DataFormats.Text; |
|
||||
if (type == NSPasteboard.NSFilenamesType) |
|
||||
return DataFormats.FileNames; |
|
||||
return type; |
|
||||
} |
|
||||
|
|
||||
public string GetText() |
|
||||
{ |
|
||||
return _info.DraggingPasteboard.GetStringForType(NSPasteboard.NSStringType); |
|
||||
} |
|
||||
|
|
||||
public IEnumerable<string> GetFileNames() |
|
||||
{ |
|
||||
using(var fileNames = (NSArray)_info.DraggingPasteboard.GetPropertyListForType(NSPasteboard.NSFilenamesType)) |
|
||||
{ |
|
||||
if (fileNames != null) |
|
||||
return NSArray.StringArrayFromHandle(fileNames.Handle); |
|
||||
} |
|
||||
|
|
||||
return Enumerable.Empty<string>(); |
|
||||
} |
|
||||
|
|
||||
public bool Contains(string dataFormat) |
|
||||
{ |
|
||||
return GetDataFormats().Any(f => f == dataFormat); |
|
||||
} |
|
||||
|
|
||||
public object Get(string dataFormat) |
|
||||
{ |
|
||||
if (dataFormat == DataFormats.Text) |
|
||||
return GetText(); |
|
||||
if (dataFormat == DataFormats.FileNames) |
|
||||
return GetFileNames(); |
|
||||
|
|
||||
return _info.DraggingPasteboard.GetDataForType(dataFormat).ToArray(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,106 +0,0 @@ |
|||||
using System; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Threading; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.CoreGraphics; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class EmulatedFramebuffer : ILockedFramebuffer |
|
||||
{ |
|
||||
private readonly TopLevelImpl.TopLevelView _view; |
|
||||
private readonly CGSize _logicalSize; |
|
||||
private readonly bool _isDeferred; |
|
||||
private readonly IUnmanagedBlob _blob; |
|
||||
|
|
||||
[DllImport("libc")] |
|
||||
static extern void memset(IntPtr p, int c, IntPtr size); |
|
||||
|
|
||||
public EmulatedFramebuffer(TopLevelImpl.TopLevelView view) |
|
||||
{ |
|
||||
_view = view; |
|
||||
|
|
||||
_isDeferred = !Dispatcher.UIThread.CheckAccess(); |
|
||||
_logicalSize = _view.LogicalSize; |
|
||||
var pixelSize = _view.PixelSize; |
|
||||
Width = (int)pixelSize.Width; |
|
||||
Height = (int)pixelSize.Height; |
|
||||
RowBytes = Width * 4; |
|
||||
Dpi = new Vector(96 * pixelSize.Width / _logicalSize.Width, 96 * pixelSize.Height / _logicalSize.Height); |
|
||||
Format = PixelFormat.Rgba8888; |
|
||||
var size = Height * RowBytes; |
|
||||
_blob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(size); |
|
||||
memset(Address, 0, new IntPtr(size)); |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
if (_blob.IsDisposed) |
|
||||
return; |
|
||||
var nfo = (int) CGBitmapFlags.ByteOrder32Big | (int) CGImageAlphaInfo.PremultipliedLast; |
|
||||
CGImage image = null; |
|
||||
try |
|
||||
{ |
|
||||
using (var colorSpace = CGColorSpace.CreateDeviceRGB()) |
|
||||
using (var bContext = new CGBitmapContext(Address, Width, Height, 8, Width * 4, |
|
||||
colorSpace, (CGImageAlphaInfo)nfo)) |
|
||||
image = bContext.ToImage(); |
|
||||
lock (_view.SyncRoot) |
|
||||
{ |
|
||||
if(!_isDeferred) |
|
||||
{ |
|
||||
using (var nscontext = NSGraphicsContext.CurrentContext) |
|
||||
using (var context = nscontext.GraphicsPort) |
|
||||
{ |
|
||||
context.SetFillColor(255, 255, 255, 255); |
|
||||
context.FillRect(new CGRect(default(CGPoint), _view.LogicalSize)); |
|
||||
context.TranslateCTM(0, _view.LogicalSize.Height - _logicalSize.Height); |
|
||||
context.DrawImage(new CGRect(default(CGPoint), _logicalSize), image); |
|
||||
context.Flush(); |
|
||||
nscontext.FlushGraphics(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
if (image != null) |
|
||||
{ |
|
||||
if (!_isDeferred) |
|
||||
image.Dispose(); |
|
||||
else |
|
||||
_view.SetBackBufferImage(new SavedImage(image, _logicalSize)); |
|
||||
} |
|
||||
_blob.Dispose(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
|
|
||||
public IntPtr Address => _blob.Address; |
|
||||
public int Width { get; } |
|
||||
public int Height { get; } |
|
||||
public int RowBytes { get; } |
|
||||
public Vector Dpi { get; } |
|
||||
public PixelFormat Format { get; } |
|
||||
} |
|
||||
|
|
||||
class SavedImage : IDisposable |
|
||||
{ |
|
||||
public CGImage Image { get; private set; } |
|
||||
public CGSize LogicalSize { get; } |
|
||||
|
|
||||
public SavedImage(CGImage image, CGSize logicalSize) |
|
||||
{ |
|
||||
Image = image; |
|
||||
LogicalSize = logicalSize; |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
Image?.Dispose(); |
|
||||
Image = null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,40 +0,0 @@ |
|||||
using System; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.CoreGraphics; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
static class Helpers |
|
||||
{ |
|
||||
public static Point ToAvaloniaPoint(this CGPoint point) => new Point(point.X, point.Y); |
|
||||
public static CGPoint ToMonoMacPoint(this Point point) => new CGPoint(point.X, point.Y); |
|
||||
public static Size ToAvaloniaSize(this CGSize size) => new Size(size.Width, size.Height); |
|
||||
public static CGSize ToMonoMacSize(this Size size) => new CGSize(size.Width, size.Height); |
|
||||
public static Rect ToAvaloniaRect(this CGRect rect) => new Rect(rect.Left, rect.Top, rect.Width, rect.Height); |
|
||||
public static CGRect ToMonoMacRect(this Rect rect) => new CGRect(rect.X, rect.Y, rect.Width, rect.Height); |
|
||||
|
|
||||
public static Point ConvertPointY(this Point pt) |
|
||||
{ |
|
||||
var sw = NSScreen.Screens[0].Frame; |
|
||||
var t = Math.Max(sw.Top, sw.Bottom); |
|
||||
return pt.WithY(t - pt.Y); |
|
||||
} |
|
||||
|
|
||||
public static CGPoint ConvertPointY(this CGPoint pt) |
|
||||
{ |
|
||||
var sw = NSScreen.Screens[0].Frame; |
|
||||
var t = Math.Max(sw.Top, sw.Bottom); |
|
||||
return new CGPoint(pt.X, t - pt.Y); |
|
||||
} |
|
||||
|
|
||||
public static Rect ConvertRectY(this Rect rect) |
|
||||
{ |
|
||||
return new Rect(rect.Position.WithY(rect.Y + rect.Height).ConvertPointY(), rect.Size); |
|
||||
} |
|
||||
|
|
||||
public static CGRect ConvertRectY(this CGRect rect) |
|
||||
{ |
|
||||
return new CGRect(new CGPoint(rect.X, rect.Y + rect.Height).ConvertPointY(), rect.Size); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,267 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using Avalonia.Input; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
public static class KeyTransform |
|
||||
{ |
|
||||
// See /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
|
|
||||
// ReSharper disable InconsistentNaming
|
|
||||
// ReSharper disable UnusedMember.Local
|
|
||||
private const int kVK_ANSI_A = 0x00; |
|
||||
private const int kVK_ANSI_S = 0x01; |
|
||||
private const int kVK_ANSI_D = 0x02; |
|
||||
private const int kVK_ANSI_F = 0x03; |
|
||||
private const int kVK_ANSI_H = 0x04; |
|
||||
private const int kVK_ANSI_G = 0x05; |
|
||||
private const int kVK_ANSI_Z = 0x06; |
|
||||
private const int kVK_ANSI_X = 0x07; |
|
||||
private const int kVK_ANSI_C = 0x08; |
|
||||
private const int kVK_ANSI_V = 0x09; |
|
||||
private const int kVK_ANSI_B = 0x0B; |
|
||||
private const int kVK_ANSI_Q = 0x0C; |
|
||||
private const int kVK_ANSI_W = 0x0D; |
|
||||
private const int kVK_ANSI_E = 0x0E; |
|
||||
private const int kVK_ANSI_R = 0x0F; |
|
||||
private const int kVK_ANSI_Y = 0x10; |
|
||||
private const int kVK_ANSI_T = 0x11; |
|
||||
private const int kVK_ANSI_1 = 0x12; |
|
||||
private const int kVK_ANSI_2 = 0x13; |
|
||||
private const int kVK_ANSI_3 = 0x14; |
|
||||
private const int kVK_ANSI_4 = 0x15; |
|
||||
private const int kVK_ANSI_6 = 0x16; |
|
||||
private const int kVK_ANSI_5 = 0x17; |
|
||||
private const int kVK_ANSI_Equal = 0x18; |
|
||||
private const int kVK_ANSI_9 = 0x19; |
|
||||
private const int kVK_ANSI_7 = 0x1A; |
|
||||
private const int kVK_ANSI_Minus = 0x1B; |
|
||||
private const int kVK_ANSI_8 = 0x1C; |
|
||||
private const int kVK_ANSI_0 = 0x1D; |
|
||||
private const int kVK_ANSI_RightBracket = 0x1E; |
|
||||
private const int kVK_ANSI_O = 0x1F; |
|
||||
private const int kVK_ANSI_U = 0x20; |
|
||||
private const int kVK_ANSI_LeftBracket = 0x21; |
|
||||
private const int kVK_ANSI_I = 0x22; |
|
||||
private const int kVK_ANSI_P = 0x23; |
|
||||
private const int kVK_ANSI_L = 0x25; |
|
||||
private const int kVK_ANSI_J = 0x26; |
|
||||
private const int kVK_ANSI_Quote = 0x27; |
|
||||
private const int kVK_ANSI_K = 0x28; |
|
||||
private const int kVK_ANSI_Semicolon = 0x29; |
|
||||
private const int kVK_ANSI_Backslash = 0x2A; |
|
||||
private const int kVK_ANSI_Comma = 0x2B; |
|
||||
private const int kVK_ANSI_Slash = 0x2C; |
|
||||
private const int kVK_ANSI_N = 0x2D; |
|
||||
private const int kVK_ANSI_M = 0x2E; |
|
||||
private const int kVK_ANSI_Period = 0x2F; |
|
||||
private const int kVK_ANSI_Grave = 0x32; |
|
||||
private const int kVK_ANSI_KeypadDecimal = 0x41; |
|
||||
private const int kVK_ANSI_KeypadMultiply = 0x43; |
|
||||
private const int kVK_ANSI_KeypadPlus = 0x45; |
|
||||
private const int kVK_ANSI_KeypadClear = 0x47; |
|
||||
private const int kVK_ANSI_KeypadDivide = 0x4B; |
|
||||
private const int kVK_ANSI_KeypadEnter = 0x4C; |
|
||||
private const int kVK_ANSI_KeypadMinus = 0x4E; |
|
||||
private const int kVK_ANSI_KeypadEquals = 0x51; |
|
||||
private const int kVK_ANSI_Keypad0 = 0x52; |
|
||||
private const int kVK_ANSI_Keypad1 = 0x53; |
|
||||
private const int kVK_ANSI_Keypad2 = 0x54; |
|
||||
private const int kVK_ANSI_Keypad3 = 0x55; |
|
||||
private const int kVK_ANSI_Keypad4 = 0x56; |
|
||||
private const int kVK_ANSI_Keypad5 = 0x57; |
|
||||
private const int kVK_ANSI_Keypad6 = 0x58; |
|
||||
private const int kVK_ANSI_Keypad7 = 0x59; |
|
||||
private const int kVK_ANSI_Keypad8 = 0x5B; |
|
||||
private const int kVK_ANSI_Keypad9 = 0x5C; |
|
||||
private const int kVK_Return = 0x24; |
|
||||
private const int kVK_Tab = 0x30; |
|
||||
private const int kVK_Space = 0x31; |
|
||||
private const int kVK_Delete = 0x33; |
|
||||
private const int kVK_Escape = 0x35; |
|
||||
private const int kVK_Command = 0x37; |
|
||||
private const int kVK_Shift = 0x38; |
|
||||
private const int kVK_CapsLock = 0x39; |
|
||||
private const int kVK_Option = 0x3A; |
|
||||
private const int kVK_Control = 0x3B; |
|
||||
private const int kVK_RightCommand = 0x36; |
|
||||
private const int kVK_RightShift = 0x3C; |
|
||||
private const int kVK_RightOption = 0x3D; |
|
||||
private const int kVK_RightControl = 0x3E; |
|
||||
private const int kVK_Function = 0x3F; |
|
||||
private const int kVK_F17 = 0x40; |
|
||||
private const int kVK_VolumeUp = 0x48; |
|
||||
private const int kVK_VolumeDown = 0x49; |
|
||||
private const int kVK_Mute = 0x4A; |
|
||||
private const int kVK_F18 = 0x4F; |
|
||||
private const int kVK_F19 = 0x50; |
|
||||
private const int kVK_F20 = 0x5A; |
|
||||
private const int kVK_F5 = 0x60; |
|
||||
private const int kVK_F6 = 0x61; |
|
||||
private const int kVK_F7 = 0x62; |
|
||||
private const int kVK_F3 = 0x63; |
|
||||
private const int kVK_F8 = 0x64; |
|
||||
private const int kVK_F9 = 0x65; |
|
||||
private const int kVK_F11 = 0x67; |
|
||||
private const int kVK_F13 = 0x69; |
|
||||
private const int kVK_F16 = 0x6A; |
|
||||
private const int kVK_F14 = 0x6B; |
|
||||
private const int kVK_F10 = 0x6D; |
|
||||
private const int kVK_F12 = 0x6F; |
|
||||
private const int kVK_F15 = 0x71; |
|
||||
private const int kVK_Help = 0x72; |
|
||||
private const int kVK_Home = 0x73; |
|
||||
private const int kVK_PageUp = 0x74; |
|
||||
private const int kVK_ForwardDelete = 0x75; |
|
||||
private const int kVK_F4 = 0x76; |
|
||||
private const int kVK_End = 0x77; |
|
||||
private const int kVK_F2 = 0x78; |
|
||||
private const int kVK_PageDown = 0x79; |
|
||||
private const int kVK_F1 = 0x7A; |
|
||||
private const int kVK_LeftArrow = 0x7B; |
|
||||
private const int kVK_RightArrow = 0x7C; |
|
||||
private const int kVK_DownArrow = 0x7D; |
|
||||
private const int kVK_UpArrow = 0x7E; |
|
||||
private const int kVK_ISO_Section = 0x0A; |
|
||||
private const int kVK_JIS_Yen = 0x5D; |
|
||||
private const int kVK_JIS_Underscore = 0x5E; |
|
||||
private const int kVK_JIS_KeypadComma = 0x5F; |
|
||||
private const int kVK_JIS_Eisu = 0x66; |
|
||||
private const int kVK_JIS_Kana = 0x68; |
|
||||
// ReSharper restore UnusedMember.Local
|
|
||||
// ReSharper restore InconsistentNaming
|
|
||||
//TODO: Map missing keys
|
|
||||
static readonly Dictionary<int, Key> Keys = new Dictionary<int, Key> |
|
||||
{ |
|
||||
[kVK_ANSI_A] = Key.A, |
|
||||
[kVK_ANSI_S] = Key.S, |
|
||||
[kVK_ANSI_D] = Key.D, |
|
||||
[kVK_ANSI_F] = Key.F, |
|
||||
[kVK_ANSI_H] = Key.H, |
|
||||
[kVK_ANSI_G] = Key.G, |
|
||||
[kVK_ANSI_Z] = Key.Z, |
|
||||
[kVK_ANSI_X] = Key.X, |
|
||||
[kVK_ANSI_C] = Key.C, |
|
||||
[kVK_ANSI_V] = Key.V, |
|
||||
[kVK_ANSI_B] = Key.B, |
|
||||
[kVK_ANSI_Q] = Key.Q, |
|
||||
[kVK_ANSI_W] = Key.W, |
|
||||
[kVK_ANSI_E] = Key.E, |
|
||||
[kVK_ANSI_R] = Key.R, |
|
||||
[kVK_ANSI_Y] = Key.Y, |
|
||||
[kVK_ANSI_T] = Key.T, |
|
||||
[kVK_ANSI_1] = Key.D1, |
|
||||
[kVK_ANSI_2] = Key.D2, |
|
||||
[kVK_ANSI_3] = Key.D3, |
|
||||
[kVK_ANSI_4] = Key.D4, |
|
||||
[kVK_ANSI_6] = Key.D6, |
|
||||
[kVK_ANSI_5] = Key.D5, |
|
||||
//[kVK_ANSI_Equal] = Key.?,
|
|
||||
[kVK_ANSI_9] = Key.D9, |
|
||||
[kVK_ANSI_7] = Key.D7, |
|
||||
[kVK_ANSI_Minus] = Key.OemMinus, |
|
||||
[kVK_ANSI_8] = Key.D8, |
|
||||
[kVK_ANSI_0] = Key.D0, |
|
||||
[kVK_ANSI_RightBracket] = Key.OemCloseBrackets, |
|
||||
[kVK_ANSI_O] = Key.O, |
|
||||
[kVK_ANSI_U] = Key.U, |
|
||||
[kVK_ANSI_LeftBracket] = Key.OemOpenBrackets, |
|
||||
[kVK_ANSI_I] = Key.I, |
|
||||
[kVK_ANSI_P] = Key.P, |
|
||||
[kVK_ANSI_L] = Key.L, |
|
||||
[kVK_ANSI_J] = Key.J, |
|
||||
[kVK_ANSI_Quote] = Key.OemQuotes, |
|
||||
[kVK_ANSI_K] = Key.K, |
|
||||
[kVK_ANSI_Semicolon] = Key.OemSemicolon, |
|
||||
[kVK_ANSI_Backslash] = Key.OemBackslash, |
|
||||
[kVK_ANSI_Comma] = Key.OemComma, |
|
||||
//[kVK_ANSI_Slash] = Key.?,
|
|
||||
[kVK_ANSI_N] = Key.N, |
|
||||
[kVK_ANSI_M] = Key.M, |
|
||||
[kVK_ANSI_Period] = Key.OemPeriod, |
|
||||
//[kVK_ANSI_Grave] = Key.?,
|
|
||||
[kVK_ANSI_KeypadDecimal] = Key.Decimal, |
|
||||
[kVK_ANSI_KeypadMultiply] = Key.Multiply, |
|
||||
[kVK_ANSI_KeypadPlus] = Key.OemPlus, |
|
||||
[kVK_ANSI_KeypadClear] = Key.Clear, |
|
||||
[kVK_ANSI_KeypadDivide] = Key.Divide, |
|
||||
[kVK_ANSI_KeypadEnter] = Key.Enter, |
|
||||
[kVK_ANSI_KeypadMinus] = Key.OemMinus, |
|
||||
//[kVK_ANSI_KeypadEquals] = Key.?,
|
|
||||
[kVK_ANSI_Keypad0] = Key.NumPad0, |
|
||||
[kVK_ANSI_Keypad1] = Key.NumPad1, |
|
||||
[kVK_ANSI_Keypad2] = Key.NumPad2, |
|
||||
[kVK_ANSI_Keypad3] = Key.NumPad3, |
|
||||
[kVK_ANSI_Keypad4] = Key.NumPad4, |
|
||||
[kVK_ANSI_Keypad5] = Key.NumPad5, |
|
||||
[kVK_ANSI_Keypad6] = Key.NumPad6, |
|
||||
[kVK_ANSI_Keypad7] = Key.NumPad7, |
|
||||
[kVK_ANSI_Keypad8] = Key.NumPad8, |
|
||||
[kVK_ANSI_Keypad9] = Key.NumPad9, |
|
||||
[kVK_Return] = Key.Return, |
|
||||
[kVK_Tab] = Key.Tab, |
|
||||
[kVK_Space] = Key.Space, |
|
||||
[kVK_Delete] = Key.Back, |
|
||||
[kVK_Escape] = Key.Escape, |
|
||||
[kVK_Command] = Key.LWin, |
|
||||
[kVK_Shift] = Key.LeftShift, |
|
||||
[kVK_CapsLock] = Key.CapsLock, |
|
||||
[kVK_Option] = Key.LeftAlt, |
|
||||
[kVK_Control] = Key.LeftCtrl, |
|
||||
[kVK_RightCommand] = Key.RWin, |
|
||||
[kVK_RightShift] = Key.RightShift, |
|
||||
[kVK_RightOption] = Key.RightAlt, |
|
||||
[kVK_RightControl] = Key.RightCtrl, |
|
||||
//[kVK_Function] = Key.?,
|
|
||||
[kVK_F17] = Key.F17, |
|
||||
[kVK_VolumeUp] = Key.VolumeUp, |
|
||||
[kVK_VolumeDown] = Key.VolumeDown, |
|
||||
[kVK_Mute] = Key.VolumeMute, |
|
||||
[kVK_F18] = Key.F18, |
|
||||
[kVK_F19] = Key.F19, |
|
||||
[kVK_F20] = Key.F20, |
|
||||
[kVK_F5] = Key.F5, |
|
||||
[kVK_F6] = Key.F6, |
|
||||
[kVK_F7] = Key.F7, |
|
||||
[kVK_F3] = Key.F3, |
|
||||
[kVK_F8] = Key.F8, |
|
||||
[kVK_F9] = Key.F9, |
|
||||
[kVK_F11] = Key.F11, |
|
||||
[kVK_F13] = Key.F13, |
|
||||
[kVK_F16] = Key.F16, |
|
||||
[kVK_F14] = Key.F14, |
|
||||
[kVK_F10] = Key.F10, |
|
||||
[kVK_F12] = Key.F12, |
|
||||
[kVK_F15] = Key.F15, |
|
||||
[kVK_Help] = Key.Help, |
|
||||
[kVK_Home] = Key.Home, |
|
||||
[kVK_PageUp] = Key.PageUp, |
|
||||
[kVK_ForwardDelete] = Key.Delete, |
|
||||
[kVK_F4] = Key.F4, |
|
||||
[kVK_End] = Key.End, |
|
||||
[kVK_F2] = Key.F2, |
|
||||
[kVK_PageDown] = Key.PageDown, |
|
||||
[kVK_F1] = Key.F1, |
|
||||
[kVK_LeftArrow] = Key.Left, |
|
||||
[kVK_RightArrow] = Key.Right, |
|
||||
[kVK_DownArrow] = Key.Down, |
|
||||
[kVK_UpArrow] = Key.Up, |
|
||||
/* |
|
||||
[kVK_ISO_Section] = Key.?, |
|
||||
[kVK_JIS_Yen] = Key.?, |
|
||||
[kVK_JIS_Underscore] = Key.?, |
|
||||
[kVK_JIS_KeypadComma] = Key.?, |
|
||||
[kVK_JIS_Eisu] = Key.?, |
|
||||
[kVK_JIS_Kana] = Key.? |
|
||||
*/ |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
public static Key? TransformKeyCode(ushort code) |
|
||||
{ |
|
||||
Key rv; |
|
||||
if (Keys.TryGetValue(code, out rv)) |
|
||||
return rv; |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,25 +0,0 @@ |
|||||
using System; |
|
||||
using Avalonia.Platform; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
public class MacScreen : Screen |
|
||||
{ |
|
||||
private readonly IntPtr handle; |
|
||||
|
|
||||
public MacScreen(Rect bounds, Rect workingArea, bool primary, IntPtr handle) : base(bounds, workingArea, primary) |
|
||||
{ |
|
||||
this.handle = handle; |
|
||||
} |
|
||||
|
|
||||
public override int GetHashCode() |
|
||||
{ |
|
||||
return (int)handle; |
|
||||
} |
|
||||
|
|
||||
public override bool Equals(object obj) |
|
||||
{ |
|
||||
return (obj is MacScreen screen) ? this.handle == screen.handle : base.Equals(obj); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,140 +0,0 @@ |
|||||
using System; |
|
||||
using System.Threading; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Controls.Platform; |
|
||||
using Avalonia.Input; |
|
||||
using Avalonia.Input.Platform; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Rendering; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.Foundation; |
|
||||
using MonoMac.ObjCRuntime; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
public class MonoMacPlatform : IWindowingPlatform, IPlatformSettings |
|
||||
{ |
|
||||
internal static MonoMacPlatform Instance { get; private set; } |
|
||||
internal readonly MouseDevice MouseDevice = new MouseDevice(); |
|
||||
readonly KeyboardDevice _keyboardDevice = new KeyboardDevice(); |
|
||||
internal static NSApplication App; |
|
||||
private static bool s_monoMacInitialized; |
|
||||
private static bool s_showInDock = true; |
|
||||
private static IRenderLoop s_renderLoop; |
|
||||
private static IRenderTimer s_renderTimer; |
|
||||
|
|
||||
void DoInitialize() |
|
||||
{ |
|
||||
InitializeMonoMac(); |
|
||||
AvaloniaLocator.CurrentMutable |
|
||||
.Bind<IStandardCursorFactory>().ToTransient<CursorFactoryStub>() |
|
||||
.Bind<IPlatformIconLoader>().ToSingleton<IconLoader>() |
|
||||
.Bind<IKeyboardDevice>().ToConstant(_keyboardDevice) |
|
||||
.Bind<IMouseDevice>().ToConstant(MouseDevice) |
|
||||
.Bind<IPlatformSettings>().ToConstant(this) |
|
||||
.Bind<IWindowingPlatform>().ToConstant(this) |
|
||||
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsImpl>() |
|
||||
.Bind<IClipboard>().ToSingleton<ClipboardImpl>() |
|
||||
.Bind<IRenderLoop>().ToConstant(s_renderLoop) |
|
||||
.Bind<IRenderTimer>().ToConstant(s_renderTimer) |
|
||||
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Windows)) |
|
||||
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance) |
|
||||
/*.Bind<IPlatformDragSource>().ToTransient<DragSource>()*/; |
|
||||
} |
|
||||
|
|
||||
public static void Initialize() |
|
||||
{ |
|
||||
Instance = new MonoMacPlatform(); |
|
||||
Instance.DoInitialize(); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// See "Using POSIX Threads in a Cocoa Application" section here:
|
|
||||
/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/20000738-125024
|
|
||||
/// </summary>
|
|
||||
class ThreadHelper : NSObject |
|
||||
{ |
|
||||
private readonly AutoResetEvent _event = new AutoResetEvent(false); |
|
||||
private const string InitThreadingName = "initThreading"; |
|
||||
[Export(InitThreadingName)] |
|
||||
public void DoNothing() |
|
||||
{ |
|
||||
_event.Set(); |
|
||||
} |
|
||||
|
|
||||
public static void InitializeCocoaThreadingLocks() |
|
||||
{ |
|
||||
var helper = new ThreadHelper(); |
|
||||
var thread = new NSThread(helper, Selector.FromHandle(Selector.GetHandle(InitThreadingName)), new NSObject()); |
|
||||
thread.Start(); |
|
||||
helper._event.WaitOne(); |
|
||||
helper._event.Dispose(); |
|
||||
if (!NSThread.IsMultiThreaded) |
|
||||
{ |
|
||||
throw new Exception("Unable to initialize Cocoa threading"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void InitializeMonoMac() |
|
||||
{ |
|
||||
if(s_monoMacInitialized) |
|
||||
return; |
|
||||
NSApplication.Init(); |
|
||||
ThreadHelper.InitializeCocoaThreadingLocks(); |
|
||||
App = NSApplication.SharedApplication; |
|
||||
UpdateActivationPolicy(); |
|
||||
s_renderLoop = new RenderLoop(); |
|
||||
s_renderTimer = new RenderTimer(60); //TODO: use CVDisplayLink
|
|
||||
s_monoMacInitialized = true; |
|
||||
} |
|
||||
|
|
||||
static void UpdateActivationPolicy() => App.ActivationPolicy = ShowInDock |
|
||||
? NSApplicationActivationPolicy.Regular |
|
||||
: NSApplicationActivationPolicy.Accessory; |
|
||||
|
|
||||
public static bool ShowInDock |
|
||||
{ |
|
||||
get => s_showInDock; |
|
||||
set |
|
||||
{ |
|
||||
s_showInDock = value; |
|
||||
if (s_monoMacInitialized) |
|
||||
UpdateActivationPolicy(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static bool UseDeferredRendering { get; set; } = true; |
|
||||
|
|
||||
public Size DoubleClickSize => new Size(4, 4); |
|
||||
public TimeSpan DoubleClickTime => TimeSpan.FromSeconds(NSEvent.DoubleClickInterval); |
|
||||
|
|
||||
public IWindowImpl CreateWindow() => new WindowImpl(); |
|
||||
|
|
||||
public IEmbeddableWindowImpl CreateEmbeddableWindow() |
|
||||
{ |
|
||||
throw new PlatformNotSupportedException(); |
|
||||
} |
|
||||
|
|
||||
public IPopupImpl CreatePopup() |
|
||||
{ |
|
||||
return new PopupImpl(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
namespace Avalonia |
|
||||
{ |
|
||||
public static class MonoMacPlatformExtensions |
|
||||
{ |
|
||||
public static T UseMonoMac<T>(this T builder, bool? useDeferredRendering = null) where T : AppBuilderBase<T>, new() |
|
||||
{ |
|
||||
if (useDeferredRendering.HasValue) |
|
||||
MonoMac.MonoMacPlatform.UseDeferredRendering = useDeferredRendering.Value; |
|
||||
return builder.UseWindowingSubsystem(MonoMac.MonoMacPlatform.Initialize, "MonoMac"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,74 +0,0 @@ |
|||||
using System; |
|
||||
using System.Threading; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Threading; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.CoreGraphics; |
|
||||
using MonoMac.Foundation; |
|
||||
using MonoMac.ObjCRuntime; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class PlatformThreadingInterface : NSObject, IPlatformThreadingInterface |
|
||||
{ |
|
||||
private bool _signaled; |
|
||||
private const string SignaledSelectorName = "avaloniauiSignaled"; |
|
||||
private readonly Selector _signaledSelector = new Selector(SignaledSelectorName); |
|
||||
public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface(); |
|
||||
public bool CurrentThreadIsLoopThread => NSThread.Current.IsMainThread; |
|
||||
|
|
||||
public event Action<DispatcherPriority?> Signaled; |
|
||||
|
|
||||
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) |
|
||||
=> NSTimer.CreateRepeatingScheduledTimer(interval, () => tick()); |
|
||||
|
|
||||
public void Signal(DispatcherPriority prio) |
|
||||
{ |
|
||||
lock (this) |
|
||||
{ |
|
||||
if (_signaled) |
|
||||
return; |
|
||||
_signaled = true; |
|
||||
} |
|
||||
PerformSelector(_signaledSelector, NSThread.MainThread, this, false, |
|
||||
new[] |
|
||||
{ |
|
||||
NSRunLoop.NSDefaultRunLoopMode, NSRunLoop.NSRunLoopEventTracking, NSRunLoop.NSRunLoopModalPanelMode, |
|
||||
NSRunLoop.NSRunLoopCommonModes, NSRunLoop.NSRunLoopConnectionReplyMode |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
[Export(SignaledSelectorName)] |
|
||||
public void CallSignaled() |
|
||||
{ |
|
||||
lock (this) |
|
||||
{ |
|
||||
if (!_signaled) |
|
||||
return; |
|
||||
_signaled = false; |
|
||||
} |
|
||||
Signaled?.Invoke(null); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
public void RunLoop(CancellationToken cancellationToken) |
|
||||
{ |
|
||||
NSApplication.SharedApplication.ActivateIgnoringOtherApps(true); |
|
||||
var app = NSApplication.SharedApplication; |
|
||||
cancellationToken.Register(() => |
|
||||
{ |
|
||||
app.PostEvent(NSEvent.OtherEvent(NSEventType.ApplicationDefined, default(CGPoint), |
|
||||
default(NSEventModifierMask), 0, 0, null, 0, 0, 0), true); |
|
||||
}); |
|
||||
while (!cancellationToken.IsCancellationRequested) |
|
||||
{ |
|
||||
var ev = app.NextEvent(NSEventMask.AnyEvent, NSDate.DistantFuture, NSRunLoop.NSDefaultRunLoopMode, true); |
|
||||
if (ev != null) |
|
||||
{ |
|
||||
app.SendEvent(ev); |
|
||||
ev.Dispose(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,30 +0,0 @@ |
|||||
using Avalonia.Platform; |
|
||||
using MonoMac.AppKit; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class PopupImpl : WindowBaseImpl, IPopupImpl |
|
||||
{ |
|
||||
public PopupImpl() |
|
||||
{ |
|
||||
UpdateStyle(); |
|
||||
Window.Level = NSWindowLevel.PopUpMenu; |
|
||||
} |
|
||||
|
|
||||
protected override NSWindowStyle GetStyle() |
|
||||
{ |
|
||||
return NSWindowStyle.Borderless; |
|
||||
} |
|
||||
|
|
||||
protected override CustomWindow CreateCustomWindow() => new CustomPopupWindow(this); |
|
||||
|
|
||||
private class CustomPopupWindow : CustomWindow |
|
||||
{ |
|
||||
public CustomPopupWindow(WindowBaseImpl impl) |
|
||||
: base(impl) |
|
||||
{ } |
|
||||
|
|
||||
public override bool WorksWhenModal() => true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,28 +0,0 @@ |
|||||
using System; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Rendering; |
|
||||
using MonoMac.Foundation; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
//TODO: Switch to using CVDisplayLink
|
|
||||
public class RenderTimer : DefaultRenderTimer |
|
||||
{ |
|
||||
public RenderTimer(int framesPerSecond) : base(framesPerSecond) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override IDisposable StartCore(Action<TimeSpan> tick) |
|
||||
{ |
|
||||
return AvaloniaLocator.Current.GetService<IRuntimePlatform>().StartSystemTimer( |
|
||||
TimeSpan.FromSeconds(1.0 / FramesPerSecond), |
|
||||
() => |
|
||||
{ |
|
||||
using (new NSAutoreleasePool()) |
|
||||
{ |
|
||||
tick?.Invoke(TimeSpan.FromMilliseconds(Environment.TickCount)); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,49 +0,0 @@ |
|||||
using Avalonia.Platform; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.Foundation; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
public class ScreenImpl : IScreenImpl |
|
||||
{ |
|
||||
private const string NSApplicationDidChangeScreenParametersNotification = "NSApplicationDidChangeScreenParametersNotification"; |
|
||||
|
|
||||
public int ScreenCount |
|
||||
{ |
|
||||
get => NSScreen.Screens.Length; |
|
||||
} |
|
||||
|
|
||||
private Screen[] _allScreens; |
|
||||
public Screen[] AllScreens |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
if (_allScreens == null) |
|
||||
{ |
|
||||
NSScreen[] screens = NSScreen.Screens; |
|
||||
Screen[] s = new Screen[screens.Length]; |
|
||||
NSScreen primary = NSScreen.MainScreen; |
|
||||
for (int i = 0; i < screens.Length; i++) |
|
||||
{ |
|
||||
Rect bounds = screens[i].Frame.ToAvaloniaRect().ConvertRectY(); |
|
||||
Rect workArea = screens[i].VisibleFrame.ToAvaloniaRect().ConvertRectY(); |
|
||||
s[i] = new MacScreen(bounds, workArea, i == 0, screens[i].Handle); |
|
||||
} |
|
||||
|
|
||||
_allScreens = s; |
|
||||
} |
|
||||
return _allScreens; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public ScreenImpl() |
|
||||
{ |
|
||||
NSNotificationCenter.DefaultCenter.AddObserver(NSApplicationDidChangeScreenParametersNotification, MonitorsChanged); |
|
||||
} |
|
||||
|
|
||||
private void MonitorsChanged(NSNotification notification) |
|
||||
{ |
|
||||
_allScreens = null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,47 +0,0 @@ |
|||||
using System.IO; |
|
||||
using Avalonia.Platform; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
// OSX doesn't have a concept of *window* icon.
|
|
||||
// Icons in the title bar are only shown if there is
|
|
||||
// an opened file (on disk) associated with the current window
|
|
||||
// see http://stackoverflow.com/a/7038671/2231814
|
|
||||
class IconLoader : IPlatformIconLoader |
|
||||
{ |
|
||||
class IconStub : IWindowIconImpl |
|
||||
{ |
|
||||
private readonly IBitmapImpl _bitmap; |
|
||||
|
|
||||
public IconStub(IBitmapImpl bitmap) |
|
||||
{ |
|
||||
_bitmap = bitmap; |
|
||||
} |
|
||||
|
|
||||
public void Save(Stream outputStream) |
|
||||
{ |
|
||||
_bitmap.Save(outputStream); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public IWindowIconImpl LoadIcon(string fileName) |
|
||||
{ |
|
||||
return new IconStub( |
|
||||
AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().LoadBitmap(fileName)); |
|
||||
} |
|
||||
|
|
||||
public IWindowIconImpl LoadIcon(Stream stream) |
|
||||
{ |
|
||||
return new IconStub( |
|
||||
AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().LoadBitmap(stream)); |
|
||||
} |
|
||||
|
|
||||
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) |
|
||||
{ |
|
||||
var ms = new MemoryStream(); |
|
||||
bitmap.Save(ms); |
|
||||
ms.Seek(0, SeekOrigin.Begin); |
|
||||
return LoadIcon(ms); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,89 +0,0 @@ |
|||||
using System.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Controls.Platform; |
|
||||
using Avalonia.Platform; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.Foundation; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class SystemDialogsImpl : ISystemDialogImpl |
|
||||
{ |
|
||||
|
|
||||
Task<string[]> RunPanel(NSSavePanel panel, IWindowImpl parent) |
|
||||
{ |
|
||||
var keyWindow = MonoMacPlatform.App.KeyWindow; |
|
||||
var tcs = new TaskCompletionSource<string[]>(); |
|
||||
void OnComplete(int result) |
|
||||
{ |
|
||||
if (result == 0) |
|
||||
tcs.SetResult(null); |
|
||||
else |
|
||||
{ |
|
||||
if (panel is NSOpenPanel openPanel) |
|
||||
tcs.SetResult(openPanel.Urls.Select(url => url.AbsoluteString.Replace("file://", "")).ToArray()); |
|
||||
else |
|
||||
tcs.SetResult(new[] { panel.Url.AbsoluteString.Replace("file://", "") }); |
|
||||
} |
|
||||
panel.OrderOut(panel); |
|
||||
keyWindow?.MakeKeyAndOrderFront(keyWindow); |
|
||||
MonoMacPlatform.App.ActivateIgnoringOtherApps(true); |
|
||||
panel.Dispose(); |
|
||||
} |
|
||||
|
|
||||
if (parent != null) |
|
||||
{ |
|
||||
var window = (WindowImpl)parent; |
|
||||
panel.BeginSheet(window.Window, OnComplete); |
|
||||
} |
|
||||
else |
|
||||
panel.Begin(OnComplete); |
|
||||
return tcs.Task; |
|
||||
} |
|
||||
|
|
||||
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) |
|
||||
{ |
|
||||
/* NOTES |
|
||||
* DefaultFileExtension is not supported |
|
||||
* Named filters are not supported |
|
||||
*/ |
|
||||
NSSavePanel panel; |
|
||||
if (dialog is OpenFileDialog openDialog) |
|
||||
{ |
|
||||
var openPanel = new NSOpenPanel(); |
|
||||
panel = openPanel; |
|
||||
|
|
||||
openPanel.AllowsMultipleSelection = openDialog.AllowMultiple; |
|
||||
} |
|
||||
else |
|
||||
panel = new NSSavePanel(); |
|
||||
panel.Title = panel.Title; |
|
||||
if (dialog.InitialDirectory != null) |
|
||||
panel.DirectoryUrl = new NSUrl(dialog.InitialDirectory); |
|
||||
if (dialog.InitialFileName != null) |
|
||||
panel.NameFieldStringValue = dialog.InitialFileName; |
|
||||
if (dialog.Filters?.Count > 0) |
|
||||
panel.AllowedFileTypes = dialog.Filters.SelectMany(f => f.Extensions).Distinct().ToArray(); |
|
||||
|
|
||||
|
|
||||
return RunPanel(panel, parent); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) |
|
||||
{ |
|
||||
var panel = new NSOpenPanel |
|
||||
{ |
|
||||
Title = dialog.Title, |
|
||||
CanChooseDirectories = true, |
|
||||
CanCreateDirectories = true, |
|
||||
CanChooseFiles = false |
|
||||
}; |
|
||||
if (dialog.DefaultDirectory != null) |
|
||||
panel.DirectoryUrl = new NSUrl(dialog.DefaultDirectory); |
|
||||
return (await RunPanel(panel, parent))?.FirstOrDefault(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,505 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using Avalonia.Controls.Platform.Surfaces; |
|
||||
using Avalonia.Input; |
|
||||
using Avalonia.Input.Raw; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Rendering; |
|
||||
using Avalonia.Threading; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.CoreGraphics; |
|
||||
using MonoMac.Foundation; |
|
||||
using MonoMac.ObjCRuntime; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
abstract class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface |
|
||||
{ |
|
||||
public TopLevelView View { get; } |
|
||||
private readonly IMouseDevice _mouse = AvaloniaLocator.Current.GetService<IMouseDevice>(); |
|
||||
private readonly IDragDropDevice _dragDevice = AvaloniaLocator.Current.GetService<IDragDropDevice>(); |
|
||||
protected TopLevelImpl() |
|
||||
{ |
|
||||
View = new TopLevelView(this); |
|
||||
} |
|
||||
|
|
||||
protected virtual void OnInput(RawInputEventArgs args) |
|
||||
{ |
|
||||
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); |
|
||||
Input?.Invoke(args); |
|
||||
} |
|
||||
|
|
||||
[Adopts("NSTextInputClient")] |
|
||||
public class TopLevelView : NSView |
|
||||
{ |
|
||||
TopLevelImpl _tl; |
|
||||
bool _isLeftPressed, _isRightPressed, _isMiddlePressed; |
|
||||
private readonly IMouseDevice _mouse; |
|
||||
private readonly IKeyboardDevice _keyboard; |
|
||||
private NSTrackingArea _area; |
|
||||
private NSCursor _cursor; |
|
||||
private bool _nonUiRedrawQueued; |
|
||||
private bool _isMouseOver; |
|
||||
|
|
||||
public CGSize PixelSize { get; set; } |
|
||||
|
|
||||
public CGSize LogicalSize { get; set; } |
|
||||
|
|
||||
private SavedImage _backBuffer; |
|
||||
public object SyncRoot { get; } = new object(); |
|
||||
|
|
||||
public TopLevelView(TopLevelImpl tl) |
|
||||
{ |
|
||||
_tl = tl; |
|
||||
_mouse = AvaloniaLocator.Current.GetService<IMouseDevice>(); |
|
||||
_keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>(); |
|
||||
|
|
||||
RegisterForDraggedTypes(new string[] { |
|
||||
"public.data" // register for any kind of data.
|
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected override void Dispose(bool disposing) |
|
||||
{ |
|
||||
if (disposing) |
|
||||
{ |
|
||||
_backBuffer?.Dispose(); |
|
||||
_backBuffer = null; |
|
||||
} |
|
||||
base.Dispose(disposing); |
|
||||
} |
|
||||
|
|
||||
public override bool ConformsToProtocol(IntPtr protocol) |
|
||||
{ |
|
||||
var rv = base.ConformsToProtocol(protocol); |
|
||||
return rv; |
|
||||
} |
|
||||
|
|
||||
public override bool IsOpaque => false; |
|
||||
|
|
||||
public override void DrawRect(CGRect dirtyRect) |
|
||||
{ |
|
||||
lock (SyncRoot) |
|
||||
_nonUiRedrawQueued = false; |
|
||||
Dispatcher.UIThread.RunJobs(DispatcherPriority.Render); |
|
||||
lock (SyncRoot) |
|
||||
{ |
|
||||
if (_backBuffer != null) |
|
||||
{ |
|
||||
using (var context = NSGraphicsContext.CurrentContext.GraphicsPort) |
|
||||
{ |
|
||||
context.SetFillColor(255, 255, 255, 255); |
|
||||
context.FillRect(new CGRect(default(CGPoint), LogicalSize)); |
|
||||
context.TranslateCTM(0, LogicalSize.Height - _backBuffer.LogicalSize.Height); |
|
||||
context.DrawImage(new CGRect(default(CGPoint), _backBuffer.LogicalSize), _backBuffer.Image); |
|
||||
context.Flush(); |
|
||||
NSGraphicsContext.CurrentContext.FlushGraphics(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
_tl.Paint?.Invoke(dirtyRect.ToAvaloniaRect()); |
|
||||
} |
|
||||
|
|
||||
public void SetBackBufferImage(SavedImage image) |
|
||||
{ |
|
||||
lock (SyncRoot) |
|
||||
{ |
|
||||
_backBuffer?.Dispose(); |
|
||||
_backBuffer = image; |
|
||||
if (image == null) |
|
||||
return; |
|
||||
|
|
||||
if (_nonUiRedrawQueued) |
|
||||
return; |
|
||||
_nonUiRedrawQueued = true; |
|
||||
Dispatcher.UIThread.Post( |
|
||||
() => |
|
||||
{ |
|
||||
lock (SyncRoot) |
|
||||
{ |
|
||||
if (!_nonUiRedrawQueued) |
|
||||
return; |
|
||||
_nonUiRedrawQueued = false; |
|
||||
} |
|
||||
SetNeedsDisplayInRect(Frame); |
|
||||
Display(); |
|
||||
}, DispatcherPriority.Render); |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Export("viewDidChangeBackingProperties:")] |
|
||||
public void ViewDidChangeBackingProperties() |
|
||||
{ |
|
||||
_tl?.ScalingChanged?.Invoke(_tl.Scaling); |
|
||||
} |
|
||||
|
|
||||
void UpdateCursor() |
|
||||
{ |
|
||||
ResetCursorRects(); |
|
||||
if (_cursor != null) |
|
||||
{ |
|
||||
AddCursorRect(Frame, _cursor); |
|
||||
if (_isMouseOver) |
|
||||
_cursor.Set(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
static readonly NSCursor ArrowCursor = NSCursor.ArrowCursor; |
|
||||
|
|
||||
public void SetCursor(NSCursor cursor) |
|
||||
{ |
|
||||
_cursor = cursor ?? ArrowCursor; |
|
||||
UpdateCursor(); |
|
||||
} |
|
||||
|
|
||||
private NSDragOperation SendRawDragEvent(NSDraggingInfo sender, RawDragEventType type) |
|
||||
{ |
|
||||
Action<RawInputEventArgs> input = _tl.Input; |
|
||||
IDragDropDevice dragDevice = _tl._dragDevice; |
|
||||
IInputRoot root = _tl?.InputRoot; |
|
||||
if (root == null || dragDevice == null || input == null) |
|
||||
return NSDragOperation.None; |
|
||||
|
|
||||
var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); |
|
||||
DraggingInfo info = new DraggingInfo(sender); |
|
||||
|
|
||||
var pt = TranslateLocalPoint(info.Location); |
|
||||
var args = new RawDragEvent(dragDevice, type, root, pt, info, dragOp, GetModifiers(NSEvent.CurrentModifierFlags)); |
|
||||
input(args); |
|
||||
return DraggingInfo.ConvertDragOperation(args.Effects); |
|
||||
} |
|
||||
|
|
||||
public override NSDragOperation DraggingEntered(NSDraggingInfo sender) |
|
||||
{ |
|
||||
return SendRawDragEvent(sender, RawDragEventType.DragEnter); |
|
||||
} |
|
||||
|
|
||||
public override NSDragOperation DraggingUpdated(NSDraggingInfo sender) |
|
||||
{ |
|
||||
return SendRawDragEvent(sender, RawDragEventType.DragOver); |
|
||||
} |
|
||||
|
|
||||
public override void DraggingExited(NSDraggingInfo sender) |
|
||||
{ |
|
||||
SendRawDragEvent(sender, RawDragEventType.DragLeave); |
|
||||
} |
|
||||
|
|
||||
public override bool PrepareForDragOperation(NSDraggingInfo sender) |
|
||||
{ |
|
||||
return SendRawDragEvent(sender, RawDragEventType.DragOver) != NSDragOperation.None; |
|
||||
} |
|
||||
|
|
||||
public override bool PerformDragOperation(NSDraggingInfo sender) |
|
||||
{ |
|
||||
return SendRawDragEvent(sender, RawDragEventType.Drop) != NSDragOperation.None; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
public override void SetFrameSize(CGSize newSize) |
|
||||
{ |
|
||||
lock (SyncRoot) |
|
||||
{ |
|
||||
base.SetFrameSize(newSize); |
|
||||
LogicalSize = Frame.Size; |
|
||||
PixelSize = ConvertSizeToBacking(LogicalSize); |
|
||||
} |
|
||||
|
|
||||
if (_area != null) |
|
||||
{ |
|
||||
RemoveTrackingArea(_area); |
|
||||
_area.Dispose(); |
|
||||
} |
|
||||
_area = new NSTrackingArea(new CGRect(default(CGPoint), newSize), |
|
||||
NSTrackingAreaOptions.ActiveAlways | |
|
||||
NSTrackingAreaOptions.MouseMoved | |
|
||||
NSTrackingAreaOptions.EnabledDuringMouseDrag, this, null); |
|
||||
AddTrackingArea(_area); |
|
||||
UpdateCursor(); |
|
||||
_tl?.Resized?.Invoke(_tl.ClientSize); |
|
||||
Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); |
|
||||
} |
|
||||
|
|
||||
InputModifiers GetModifiers(NSEventModifierMask mod) |
|
||||
{ |
|
||||
var rv = new InputModifiers(); |
|
||||
if (mod.HasFlag(NSEventModifierMask.ControlKeyMask)) |
|
||||
rv |= InputModifiers.Control; |
|
||||
if (mod.HasFlag(NSEventModifierMask.ShiftKeyMask)) |
|
||||
rv |= InputModifiers.Shift; |
|
||||
if (mod.HasFlag(NSEventModifierMask.AlternateKeyMask)) |
|
||||
rv |= InputModifiers.Alt; |
|
||||
if (mod.HasFlag(NSEventModifierMask.CommandKeyMask)) |
|
||||
rv |= InputModifiers.Windows; |
|
||||
|
|
||||
if (_isLeftPressed) |
|
||||
rv |= InputModifiers.LeftMouseButton; |
|
||||
if (_isMiddlePressed) |
|
||||
rv |= InputModifiers.MiddleMouseButton; |
|
||||
if (_isRightPressed) |
|
||||
rv |= InputModifiers.RightMouseButton; |
|
||||
return rv; |
|
||||
} |
|
||||
|
|
||||
public Point TranslateLocalPoint(Point pt) => pt.WithY(Bounds.Height - pt.Y); |
|
||||
|
|
||||
Vector GetDelta(NSEvent ev) |
|
||||
{ |
|
||||
var rv = new Vector(ev.ScrollingDeltaX, ev.ScrollingDeltaY); |
|
||||
//TODO: Verify if handling of HasPreciseScrollingDeltas
|
|
||||
// is required (touchpad or magic-mouse is needed)
|
|
||||
return rv; |
|
||||
} |
|
||||
|
|
||||
uint GetTimeStamp(NSEvent ev) => (uint) (ev.Timestamp * 1000); |
|
||||
|
|
||||
void MouseEvent(NSEvent ev, RawMouseEventType type) |
|
||||
{ |
|
||||
BecomeFirstResponder(); |
|
||||
var loc = TranslateLocalPoint(ConvertPointToView(ev.LocationInWindow, this).ToAvaloniaPoint()); |
|
||||
var ts = GetTimeStamp(ev); |
|
||||
var mod = GetModifiers(ev.ModifierFlags); |
|
||||
if (type == RawMouseEventType.Wheel) |
|
||||
{ |
|
||||
var delta = GetDelta(ev); |
|
||||
// ReSharper disable CompareOfFloatsByEqualityOperator
|
|
||||
if (delta.X == 0 && delta.Y == 0) |
|
||||
return; |
|
||||
// ReSharper restore CompareOfFloatsByEqualityOperator
|
|
||||
_tl.OnInput(new RawMouseWheelEventArgs(_mouse, ts, _tl.InputRoot, loc, |
|
||||
delta, mod)); |
|
||||
} |
|
||||
else |
|
||||
_tl.OnInput(new RawMouseEventArgs(_mouse, ts, _tl.InputRoot, type, loc, mod)); |
|
||||
} |
|
||||
|
|
||||
public override void MouseMoved(NSEvent theEvent) |
|
||||
{ |
|
||||
MouseEvent(theEvent, RawMouseEventType.Move); |
|
||||
base.MouseMoved(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void MouseDragged(NSEvent theEvent) |
|
||||
{ |
|
||||
MouseEvent(theEvent, RawMouseEventType.Move); |
|
||||
base.MouseDragged(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void OtherMouseDragged(NSEvent theEvent) |
|
||||
{ |
|
||||
MouseEvent(theEvent, RawMouseEventType.Move); |
|
||||
base.OtherMouseDragged(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void RightMouseDragged(NSEvent theEvent) |
|
||||
{ |
|
||||
MouseEvent(theEvent, RawMouseEventType.Move); |
|
||||
base.RightMouseDragged(theEvent); |
|
||||
} |
|
||||
|
|
||||
public NSEvent LastMouseDownEvent { get; private set; } |
|
||||
|
|
||||
public override void MouseDown(NSEvent theEvent) |
|
||||
{ |
|
||||
_isLeftPressed = true; |
|
||||
LastMouseDownEvent = theEvent; |
|
||||
MouseEvent(theEvent, RawMouseEventType.LeftButtonDown); |
|
||||
LastMouseDownEvent = null; |
|
||||
base.MouseDown(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void RightMouseDown(NSEvent theEvent) |
|
||||
{ |
|
||||
_isRightPressed = true; |
|
||||
MouseEvent(theEvent, RawMouseEventType.RightButtonDown); |
|
||||
base.RightMouseDown(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void OtherMouseDown(NSEvent theEvent) |
|
||||
{ |
|
||||
_isMiddlePressed = true; |
|
||||
MouseEvent(theEvent, RawMouseEventType.MiddleButtonDown); |
|
||||
base.OtherMouseDown(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void MouseUp(NSEvent theEvent) |
|
||||
{ |
|
||||
_isLeftPressed = false; |
|
||||
MouseEvent(theEvent, RawMouseEventType.LeftButtonUp); |
|
||||
base.MouseUp(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void RightMouseUp(NSEvent theEvent) |
|
||||
{ |
|
||||
_isRightPressed = false; |
|
||||
MouseEvent(theEvent, RawMouseEventType.RightButtonUp); |
|
||||
base.RightMouseUp(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void OtherMouseUp(NSEvent theEvent) |
|
||||
{ |
|
||||
_isMiddlePressed = false; |
|
||||
MouseEvent(theEvent, RawMouseEventType.MiddleButtonUp); |
|
||||
base.OtherMouseUp(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void ScrollWheel(NSEvent theEvent) |
|
||||
{ |
|
||||
MouseEvent(theEvent, RawMouseEventType.Wheel); |
|
||||
base.ScrollWheel(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void MouseExited(NSEvent theEvent) |
|
||||
{ |
|
||||
_isMouseOver = false; |
|
||||
MouseEvent(theEvent, RawMouseEventType.LeaveWindow); |
|
||||
base.MouseExited(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void MouseEntered(NSEvent theEvent) |
|
||||
{ |
|
||||
_isMouseOver = true; |
|
||||
base.MouseEntered(theEvent); |
|
||||
} |
|
||||
|
|
||||
void KeyboardEvent(RawKeyEventType type, NSEvent ev) |
|
||||
{ |
|
||||
var code = KeyTransform.TransformKeyCode(ev.KeyCode); |
|
||||
if (!code.HasValue) |
|
||||
return; |
|
||||
_tl.OnInput(new RawKeyEventArgs(_keyboard, GetTimeStamp(ev), |
|
||||
type, code.Value, GetModifiers(ev.ModifierFlags))); |
|
||||
} |
|
||||
|
|
||||
public override void KeyDown(NSEvent theEvent) |
|
||||
{ |
|
||||
KeyboardEvent(RawKeyEventType.KeyDown, theEvent); |
|
||||
InputContext.HandleEvent(theEvent); |
|
||||
base.KeyDown(theEvent); |
|
||||
} |
|
||||
|
|
||||
public override void KeyUp(NSEvent theEvent) |
|
||||
{ |
|
||||
KeyboardEvent(RawKeyEventType.KeyUp, theEvent); |
|
||||
base.KeyUp(theEvent); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
#region NSTextInputClient
|
|
||||
|
|
||||
public override bool AcceptsFirstResponder() => true; |
|
||||
|
|
||||
public bool HasMarkedText |
|
||||
{ |
|
||||
[Export("hasMarkedText")] get => false; |
|
||||
} |
|
||||
|
|
||||
public NSRange MarkedRange |
|
||||
{ |
|
||||
[Export("markedRange")] get => new NSRange(NSRange.NotFound, 0); |
|
||||
} |
|
||||
|
|
||||
public NSRange SelectedRange |
|
||||
{ |
|
||||
[Export("selectedRange")] get => new NSRange(NSRange.NotFound, 0); |
|
||||
} |
|
||||
|
|
||||
[Export("setMarkedText:selectedRange:replacementRange:")] |
|
||||
public void SetMarkedText(NSString str, NSRange a1, NSRange a2) |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
|
|
||||
[Export("unmarkText")] |
|
||||
public void UnmarkText() |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
|
|
||||
public NSArray ValidAttributesForMarkedText |
|
||||
{ |
|
||||
[Export("validAttributesForMarkedText")] get => new NSArray(); |
|
||||
} |
|
||||
|
|
||||
[Export("attributedSubstringForProposedRange:actualRange:")] |
|
||||
public NSAttributedString AttributedSubstringForProposedRange(NSRange range, IntPtr wat) |
|
||||
{ |
|
||||
return new NSAttributedString(""); |
|
||||
} |
|
||||
|
|
||||
[Export("insertText:replacementRange:")] |
|
||||
public void InsertText(NSString str, NSRange range) |
|
||||
{ |
|
||||
//TODO: timestamp
|
|
||||
_tl.OnInput(new RawTextInputEventArgs(_keyboard, 0, str.ToString())); |
|
||||
} |
|
||||
|
|
||||
[Export("characterIndexForPoint:")] |
|
||||
public uint CharacterIndexForPoint(CGPoint pt) |
|
||||
{ |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
[Export("firstRectForCharacterRange:actualRange:")] |
|
||||
public CGRect FirstRectForCharacterRange(NSRange range, IntPtr wat) |
|
||||
{ |
|
||||
return new CGRect(); |
|
||||
} |
|
||||
|
|
||||
#endregion
|
|
||||
} |
|
||||
|
|
||||
public IInputRoot InputRoot { get; private set; } |
|
||||
|
|
||||
public abstract Size ClientSize { get; } |
|
||||
|
|
||||
public double Scaling |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
if (View.Window == null) |
|
||||
return 1; |
|
||||
return View.Window.BackingScaleFactor; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public IEnumerable<object> Surfaces => new[] {this}; |
|
||||
public IMouseDevice MouseDevice => _mouse; |
|
||||
|
|
||||
public Action<RawInputEventArgs> Input { get; set; } |
|
||||
public Action<Rect> Paint { get; set; } |
|
||||
public Action<Size> Resized { get; set; } |
|
||||
public Action<double> ScalingChanged { get; set; } |
|
||||
public Action Closed { get; set; } |
|
||||
|
|
||||
|
|
||||
public virtual void Dispose() |
|
||||
{ |
|
||||
Closed?.Invoke(); |
|
||||
Closed = null; |
|
||||
View.Dispose(); |
|
||||
} |
|
||||
|
|
||||
public IRenderer CreateRenderer(IRenderRoot root) => |
|
||||
MonoMacPlatform.UseDeferredRendering |
|
||||
? new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>()) |
|
||||
: (IRenderer) new ImmediateRenderer(root); |
|
||||
|
|
||||
public void Invalidate(Rect rect) |
|
||||
{ |
|
||||
if (!MonoMacPlatform.UseDeferredRendering) |
|
||||
View.SetNeedsDisplayInRect(View.Frame); |
|
||||
} |
|
||||
|
|
||||
public abstract Point PointToClient(Point point); |
|
||||
|
|
||||
public abstract Point PointToScreen(Point point); |
|
||||
|
|
||||
public void SetCursor(IPlatformHandle cursor) => View.SetCursor((cursor as Cursor)?.Native); |
|
||||
|
|
||||
public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot; |
|
||||
|
|
||||
public ILockedFramebuffer Lock() => new EmulatedFramebuffer(View); |
|
||||
} |
|
||||
} |
|
||||
@ -1,208 +0,0 @@ |
|||||
using System; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Input.Raw; |
|
||||
using Avalonia.Platform; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.CoreGraphics; |
|
||||
using MonoMac.Foundation; |
|
||||
using MonoMac.ObjCRuntime; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class WindowBaseImpl : TopLevelImpl, IWindowBaseImpl |
|
||||
{ |
|
||||
private readonly ManagedWindowResizeDragHelper _managedDrag; |
|
||||
public CustomWindow Window { get; private set; } |
|
||||
|
|
||||
private bool _closed; |
|
||||
|
|
||||
public WindowBaseImpl() |
|
||||
{ |
|
||||
_managedDrag = new ManagedWindowResizeDragHelper(this, _ => { }, ResizeForManagedDrag); |
|
||||
// ReSharper disable once VirtualMemberCallInConstructor
|
|
||||
Window = CreateCustomWindow(); |
|
||||
Window.StyleMask = NSWindowStyle.Titled; |
|
||||
Window.BackingType = NSBackingStore.Buffered; |
|
||||
Window.ContentView = View; |
|
||||
// ReSharper disable once VirtualMemberCallInConstructor
|
|
||||
Window.Delegate = CreateWindowDelegate(); |
|
||||
} |
|
||||
|
|
||||
public class CustomWindow : NSWindow |
|
||||
{ |
|
||||
readonly WindowBaseImpl _impl; |
|
||||
|
|
||||
public CustomWindow(WindowBaseImpl impl) |
|
||||
{ |
|
||||
_impl = impl; |
|
||||
} |
|
||||
|
|
||||
public override void BecomeKeyWindow() |
|
||||
{ |
|
||||
_impl.Activated?.Invoke(); |
|
||||
base.BecomeKeyWindow(); |
|
||||
} |
|
||||
|
|
||||
public override void ResignKeyWindow() |
|
||||
{ |
|
||||
_impl.Deactivated?.Invoke(); |
|
||||
base.ResignKeyWindow(); |
|
||||
} |
|
||||
|
|
||||
private bool _canBecomeKeyAndMain; |
|
||||
public override bool CanBecomeKeyWindow => _canBecomeKeyAndMain; |
|
||||
public override bool CanBecomeMainWindow => _canBecomeKeyAndMain; |
|
||||
|
|
||||
public void SetCanBecomeKeyAndMain() => _canBecomeKeyAndMain = true; |
|
||||
} |
|
||||
|
|
||||
protected virtual CustomWindow CreateCustomWindow() => new CustomWindow(this); |
|
||||
protected virtual NSWindowDelegate CreateWindowDelegate() => new WindowBaseDelegate(this); |
|
||||
|
|
||||
public class WindowBaseDelegate : NSWindowDelegate |
|
||||
{ |
|
||||
readonly WindowBaseImpl _impl; |
|
||||
public WindowBaseDelegate(WindowBaseImpl impl) |
|
||||
{ |
|
||||
_impl = impl; |
|
||||
} |
|
||||
|
|
||||
public override void DidMoved(global::MonoMac.Foundation.NSNotification notification) |
|
||||
{ |
|
||||
_impl.PositionChanged?.Invoke(_impl.Position); |
|
||||
} |
|
||||
|
|
||||
public override bool WindowShouldClose(NSObject sender) |
|
||||
{ |
|
||||
bool? preventClose = _impl.Closing?.Invoke(); |
|
||||
return preventClose != true; |
|
||||
} |
|
||||
|
|
||||
public override void WillClose(global::MonoMac.Foundation.NSNotification notification) |
|
||||
{ |
|
||||
_impl._closed = true; |
|
||||
_impl.Closed?.Invoke(); |
|
||||
} |
|
||||
|
|
||||
public override CGRect WillUseStandardFrame(NSWindow window, CGRect newFrame) |
|
||||
{ |
|
||||
if (_impl is WindowImpl w && w.UndecoratedIsMaximized && w.UndecoratedLastUnmaximizedFrame.HasValue) |
|
||||
return w.UndecoratedLastUnmaximizedFrame.Value; |
|
||||
return window.Screen.VisibleFrame; |
|
||||
} |
|
||||
|
|
||||
public override bool ShouldZoom(NSWindow window, CGRect newFrame) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
public override void DidResize(NSNotification notification) |
|
||||
{ |
|
||||
_impl.OnResized(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// As you can't combine NSWindowDelegate overrides and events this is a workaround
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnResized() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public Point Position |
|
||||
{ |
|
||||
get => Window.Frame.ToAvaloniaRect().BottomLeft.ConvertPointY(); |
|
||||
set => Window.SetFrameTopLeftPoint(value.ToMonoMacPoint().ConvertPointY()); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
protected virtual NSWindowStyle GetStyle() => NSWindowStyle.Borderless; |
|
||||
|
|
||||
protected void UpdateStyle() => Window.StyleMask = GetStyle(); |
|
||||
|
|
||||
|
|
||||
IPlatformHandle IWindowBaseImpl.Handle => new PlatformHandle(Window.Handle, "NSWindow"); |
|
||||
public Size MaxClientSize => NSScreen.Screens[0].Frame.ToAvaloniaRect().Size; |
|
||||
public Action<Point> PositionChanged { get; set; } |
|
||||
public Action Deactivated { get; set; } |
|
||||
public Action Activated { get; set; } |
|
||||
public Func<bool> Closing { get; set; } |
|
||||
|
|
||||
public override Size ClientSize => Window.ContentRectFor(Window.Frame).Size.ToAvaloniaSize(); |
|
||||
|
|
||||
|
|
||||
public void Show() => Window.MakeKeyAndOrderFront(Window); |
|
||||
|
|
||||
public void Hide() => Window?.OrderOut(Window); |
|
||||
|
|
||||
public void SetTopmost(bool value) => Window.Level = value ? NSWindowLevel.Floating : NSWindowLevel.Normal; |
|
||||
|
|
||||
public void BeginMoveDrag() |
|
||||
{ |
|
||||
var ev = View.LastMouseDownEvent; |
|
||||
if (ev == null) |
|
||||
return; |
|
||||
var handle = Selector.GetHandle("performWindowDragWithEvent:"); |
|
||||
Messaging.void_objc_msgSend_IntPtr(Window.Handle, handle, ev.Handle); |
|
||||
} |
|
||||
|
|
||||
public void BeginResizeDrag(WindowEdge edge) |
|
||||
{ |
|
||||
var screenPoint = NSEvent.CurrentMouseLocation.ConvertPointY().ToAvaloniaPoint(); |
|
||||
_managedDrag.BeginResizeDrag(edge, PointToClient(screenPoint)); |
|
||||
} |
|
||||
|
|
||||
protected override void OnInput(RawInputEventArgs args) |
|
||||
{ |
|
||||
if (_managedDrag.PreprocessInputEvent(ref args)) |
|
||||
return; |
|
||||
base.OnInput(args); |
|
||||
} |
|
||||
|
|
||||
public void Activate() => Window.MakeKeyWindow(); |
|
||||
|
|
||||
public void ResizeForManagedDrag(Rect rc) |
|
||||
{ |
|
||||
var frame = new CGRect(rc.X, rc.Position.ConvertPointY().Y - rc.Height, rc.Width, rc.Height); |
|
||||
Window.SetFrame(frame, true); |
|
||||
} |
|
||||
|
|
||||
public void Resize(Size clientSize) |
|
||||
{ |
|
||||
var pos = Position; |
|
||||
Window.SetContentSize(clientSize.ToMonoMacSize()); |
|
||||
Position = pos; |
|
||||
} |
|
||||
|
|
||||
public void SetMinMaxSize(Size minSize, Size maxSize) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public IScreenImpl Screen |
|
||||
{ |
|
||||
get; |
|
||||
} = new ScreenImpl(); |
|
||||
|
|
||||
public override Point PointToClient(Point point) |
|
||||
{ |
|
||||
var cocoaScreenPoint = point.ToMonoMacPoint().ConvertPointY(); |
|
||||
var cocoaViewPoint = Window.ConvertScreenToBase(cocoaScreenPoint).ToAvaloniaPoint(); |
|
||||
return View.TranslateLocalPoint(cocoaViewPoint); |
|
||||
} |
|
||||
|
|
||||
public override Point PointToScreen(Point point) |
|
||||
{ |
|
||||
var cocoaViewPoint = View.TranslateLocalPoint(point).ToMonoMacPoint(); |
|
||||
var cocoaScreenPoint = Window.ConvertBaseToScreen(cocoaViewPoint); |
|
||||
return cocoaScreenPoint.ConvertPointY().ToAvaloniaPoint(); |
|
||||
} |
|
||||
|
|
||||
public override void Dispose() |
|
||||
{ |
|
||||
if (!_closed) |
|
||||
Window.Close(); |
|
||||
Window.Dispose(); |
|
||||
base.Dispose(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,176 +0,0 @@ |
|||||
using System; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Threading; |
|
||||
using MonoMac.AppKit; |
|
||||
using MonoMac.CoreGraphics; |
|
||||
|
|
||||
namespace Avalonia.MonoMac |
|
||||
{ |
|
||||
class WindowImpl : WindowBaseImpl, IWindowImpl |
|
||||
{ |
|
||||
public bool IsDecorated = true; |
|
||||
public bool IsResizable = true; |
|
||||
public CGRect? UndecoratedLastUnmaximizedFrame; |
|
||||
|
|
||||
private WindowState _lastWindowState; |
|
||||
|
|
||||
public WindowImpl() |
|
||||
{ |
|
||||
// Post UpdateStyle to UIThread otherwise for as yet unknown reason.
|
|
||||
// The window becomes transparent to mouse clicks except a 100x100 square
|
|
||||
// at the top left. (danwalmsley)
|
|
||||
Dispatcher.UIThread.Post(() => |
|
||||
{ |
|
||||
UpdateStyle(); |
|
||||
}); |
|
||||
|
|
||||
Window.SetCanBecomeKeyAndMain(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
protected override void OnResized() |
|
||||
{ |
|
||||
var windowState = Window.IsMiniaturized ? WindowState.Minimized |
|
||||
: (IsZoomed ? WindowState.Maximized : WindowState.Normal); |
|
||||
|
|
||||
if (windowState != _lastWindowState) |
|
||||
{ |
|
||||
_lastWindowState = windowState; |
|
||||
WindowStateChanged?.Invoke(windowState); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public WindowState WindowState |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
if (Window.IsMiniaturized) |
|
||||
return WindowState.Minimized; |
|
||||
if (IsZoomed) |
|
||||
return WindowState.Maximized; |
|
||||
return WindowState.Normal; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (value == WindowState.Maximized) |
|
||||
{ |
|
||||
if (Window.IsMiniaturized) |
|
||||
Window.Deminiaturize(Window); |
|
||||
if (!IsZoomed) |
|
||||
DoZoom(); |
|
||||
} |
|
||||
else if (value.HasFlag(WindowState.Minimized)) |
|
||||
Window.Miniaturize(Window); |
|
||||
else |
|
||||
{ |
|
||||
if (Window.IsMiniaturized) |
|
||||
Window.Deminiaturize(Window); |
|
||||
if (IsZoomed) |
|
||||
DoZoom(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public Action<WindowState> WindowStateChanged { get; set; } |
|
||||
|
|
||||
bool IsZoomed => IsDecorated ? Window.IsZoomed : UndecoratedIsMaximized; |
|
||||
|
|
||||
public bool UndecoratedIsMaximized => Window.Frame == Window.Screen.VisibleFrame; |
|
||||
|
|
||||
void DoZoom() |
|
||||
{ |
|
||||
if (IsDecorated) |
|
||||
Window.PerformZoom(Window); |
|
||||
else |
|
||||
{ |
|
||||
if (!UndecoratedIsMaximized) |
|
||||
UndecoratedLastUnmaximizedFrame = Window.Frame; |
|
||||
Window.Zoom(Window); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public void SetIcon(IWindowIconImpl icon) |
|
||||
{ |
|
||||
//No-OP, see http://stackoverflow.com/a/7038671/2231814
|
|
||||
} |
|
||||
|
|
||||
public void ShowTaskbarIcon(bool value) |
|
||||
{ |
|
||||
//No-OP, there is no such this as taskbar in OSX
|
|
||||
} |
|
||||
|
|
||||
protected override NSWindowStyle GetStyle() |
|
||||
{ |
|
||||
var windowStyle = NSWindowStyle.Borderless; |
|
||||
|
|
||||
if (IsDecorated) |
|
||||
windowStyle |= NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Titled; |
|
||||
|
|
||||
if (IsResizable) |
|
||||
windowStyle |= NSWindowStyle.Resizable; |
|
||||
|
|
||||
return windowStyle; |
|
||||
} |
|
||||
|
|
||||
public void SetSystemDecorations(bool enabled) |
|
||||
{ |
|
||||
IsDecorated = enabled; |
|
||||
UpdateStyle(); |
|
||||
} |
|
||||
|
|
||||
public void CanResize(bool value) |
|
||||
{ |
|
||||
IsResizable = value; |
|
||||
UpdateStyle(); |
|
||||
} |
|
||||
|
|
||||
public void SetTitle(string title) => Window.Title = title; |
|
||||
|
|
||||
class ModalDisposable : IDisposable |
|
||||
{ |
|
||||
readonly WindowImpl _impl; |
|
||||
readonly IntPtr _modalSession; |
|
||||
bool disposed; |
|
||||
|
|
||||
public ModalDisposable(WindowImpl impl, IntPtr modalSession) |
|
||||
{ |
|
||||
_impl = impl; |
|
||||
_modalSession = modalSession; |
|
||||
} |
|
||||
|
|
||||
public void Continue() |
|
||||
{ |
|
||||
if (disposed) |
|
||||
return; |
|
||||
|
|
||||
var response = (NSRunResponse)NSApplication.SharedApplication.RunModalSession(_modalSession); |
|
||||
if (response == NSRunResponse.Continues) |
|
||||
{ |
|
||||
Dispatcher.UIThread.Post(Continue, DispatcherPriority.ContextIdle); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
Logging.Logger.Log(Logging.LogEventLevel.Debug, "MonoMac", this, "Modal session ended"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
Logging.Logger.Log(Logging.LogEventLevel.Debug, "MonoMac", this, "ModalDisposable disposed"); |
|
||||
_impl.Window.OrderOut(_impl.Window); |
|
||||
NSApplication.SharedApplication.EndModalSession(_modalSession); |
|
||||
disposed = true; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public IDisposable ShowDialog() |
|
||||
{ |
|
||||
var session = NSApplication.SharedApplication.BeginModalSession(Window); |
|
||||
var disposable = new ModalDisposable(this, session); |
|
||||
Dispatcher.UIThread.Post(disposable.Continue, DispatcherPriority.ContextIdle); |
|
||||
|
|
||||
return disposable; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue