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