Browse Source

Merge branch 'fixes/DefaultSKTypeface' of https://github.com/Gillibald/Avalonia into fixes/DefaultSKTypeface

pull/2027/head
Benedikt Schroeder 7 years ago
parent
commit
daf55ef742
  1. 39
      Avalonia.sln
  2. 89
      azure-pipelines.yml
  3. 92
      build.cake
  4. 5
      build/MonoMac.props
  5. 4
      build/SharedVersion.props
  6. 37
      packages.cake
  7. 8
      parameters.cake
  8. 6
      readme.md
  9. 4
      src/Avalonia.DotNetCoreRuntime/AppBuilder.cs
  10. 2
      src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
  11. 9
      src/Avalonia.Native/Avalonia.Native.csproj
  12. 6
      src/OSX/Avalonia.MonoMac/AssemblyInfo.cs
  13. 20
      src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj
  14. 29
      src/OSX/Avalonia.MonoMac/ClipboardImpl.cs
  15. 66
      src/OSX/Avalonia.MonoMac/Cursor.cs
  16. 119
      src/OSX/Avalonia.MonoMac/DragSource.cs
  17. 89
      src/OSX/Avalonia.MonoMac/DraggingInfo.cs
  18. 106
      src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs
  19. 40
      src/OSX/Avalonia.MonoMac/Helpers.cs
  20. 267
      src/OSX/Avalonia.MonoMac/KeyTransform.cs
  21. 25
      src/OSX/Avalonia.MonoMac/MacScreen.cs
  22. 140
      src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs
  23. 74
      src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs
  24. 30
      src/OSX/Avalonia.MonoMac/PopupImpl.cs
  25. 28
      src/OSX/Avalonia.MonoMac/RenderTimer.cs
  26. 49
      src/OSX/Avalonia.MonoMac/ScreenImpl.cs
  27. 47
      src/OSX/Avalonia.MonoMac/Stubs.cs
  28. 89
      src/OSX/Avalonia.MonoMac/SystemDialogsImpl.cs
  29. 505
      src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
  30. 208
      src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
  31. 176
      src/OSX/Avalonia.MonoMac/WindowImpl.cs

39
Avalonia.sln

@ -80,13 +80,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skia", "Skia", "{3743B0F2-C
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.iOS", "src\iOS\Avalonia.iOS\Avalonia.iOS.csproj", "{4488AD85-1495-4809-9AA4-DDFE0A48527E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.iOS", "src\iOS\Avalonia.iOS\Avalonia.iOS.csproj", "{4488AD85-1495-4809-9AA4-DDFE0A48527E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.iOSTestApplication", "src\iOS\Avalonia.iOSTestApplication\Avalonia.iOSTestApplication.csproj", "{8C923867-8A8F-4F6B-8B80-47D9E8436166}"
EndProject
@ -164,7 +164,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.LinuxFramebuffer",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct3DInteropSample", "samples\interop\Direct3DInteropSample\Direct3DInteropSample.csproj", "{638580B0-7910-40EF-B674-DCB34DA308CD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.Skia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
EndProject
@ -178,17 +178,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Designer.HostApp",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Previewer", "samples\Previewer\Previewer.csproj", "{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OSX", "OSX", "{A59C4C0A-64DF-4621-B450-2BA00D6F61E2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MonoMac", "src\OSX\Avalonia.MonoMac\Avalonia.MonoMac.csproj", "{CBFD5788-567D-401B-9DFA-74E4224025A0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Designer.HostApp.NetFX", "src\tools\Avalonia.Designer.HostApp.NetFX\Avalonia.Designer.HostApp.NetFX.csproj", "{4ADA61C8-D191-428D-9066-EF4F0D86520F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.UnitTests", "tests\Avalonia.Skia.UnitTests\Avalonia.Skia.UnitTests.csproj", "{E1240B49-7B4B-4371-A00E-068778C5CF0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.OpenGL", "src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.OpenGL", "src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
@ -1595,30 +1591,6 @@ Global
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.Build.0 = Release|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhone.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhone.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|Any CPU.Build.0 = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhone.ActiveCfg = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhone.Build.0 = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{CBFD5788-567D-401B-9DFA-74E4224025A0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{4ADA61C8-D191-428D-9066-EF4F0D86520F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@ -1765,7 +1737,6 @@ Global
{E2999E4A-9086-401F-898C-AEB0AD38E676} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{050CC912-FF49-4A8B-B534-9544017446DD} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{CBFD5788-567D-401B-9DFA-74E4224025A0} = {A59C4C0A-64DF-4621-B450-2BA00D6F61E2}
{4ADA61C8-D191-428D-9066-EF4F0D86520F} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{E1240B49-7B4B-4371-A00E-068778C5CF0B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection

89
azure-pipelines.yml

@ -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'

92
build.cake

@ -1,15 +1,9 @@
///////////////////////////////////////////////////////////////////////////////
// ADDINS
// TOOLS
///////////////////////////////////////////////////////////////////////////////
#addin "nuget:?package=NuGet.Core&version=2.14.0"
#tool "nuget:?package=NuGet.CommandLine&version=4.7.1"
#tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.3"
///////////////////////////////////////////////////////////////////////////////
// TOOLS
///////////////////////////////////////////////////////////////////////////////
#tool "nuget:?package=xunit.runner.console&version=2.3.1"
#tool "nuget:?package=JetBrains.dotMemoryUnit&version=3.0.20171219.105559"
@ -21,7 +15,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NuGet;
///////////////////////////////////////////////////////////////////////////////
// SCRIPTS
@ -71,6 +64,7 @@ Setup<AvaloniaBuildData>(context =>
Information("IsRunningOnUnix: " + parameters.IsRunningOnUnix);
Information("IsRunningOnWindows: " + parameters.IsRunningOnWindows);
Information("IsRunningOnAppVeyor: " + parameters.IsRunningOnAppVeyor);
Information("IsRunnongOnAzure:" + parameters.IsRunningOnAzure);
Information("IsPullRequest: " + parameters.IsPullRequest);
Information("IsMainRepo: " + parameters.IsMainRepo);
Information("IsMasterBranch: " + parameters.IsMasterBranch);
@ -246,71 +240,6 @@ Task("Create-NuGet-Packages-Impl")
}
});
Task("Publish-MyGet-Impl")
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.IsLocalBuild)
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.IsPullRequest)
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsMainRepo)
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsMasterBranch)
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsMyGetRelease)
.Does<AvaloniaBuildData>(data =>
{
var apiKey = EnvironmentVariable("MYGET_API_KEY");
if(string.IsNullOrEmpty(apiKey))
{
throw new InvalidOperationException("Could not resolve MyGet API key.");
}
var apiUrl = EnvironmentVariable("MYGET_API_URL");
if(string.IsNullOrEmpty(apiUrl))
{
throw new InvalidOperationException("Could not resolve MyGet API url.");
}
foreach(var nupkg in data.Packages.NugetPackages)
{
NuGetPush(nupkg, new NuGetPushSettings {
Source = apiUrl,
ApiKey = apiKey
});
}
})
.OnError(exception =>
{
Information("Publish-MyGet Task failed, but continuing with next Task...");
});
Task("Publish-NuGet-Impl")
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.IsLocalBuild)
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.IsPullRequest)
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsMainRepo)
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsNuGetRelease)
.Does<AvaloniaBuildData>(data =>
{
var apiKey = EnvironmentVariable("NUGET_API_KEY");
if(string.IsNullOrEmpty(apiKey))
{
throw new InvalidOperationException("Could not resolve NuGet API key.");
}
var apiUrl = EnvironmentVariable("NUGET_API_URL");
if(string.IsNullOrEmpty(apiUrl))
{
throw new InvalidOperationException("Could not resolve NuGet API url.");
}
foreach(var nupkg in data.Packages.NugetPackages)
{
NuGetPush(nupkg, new NuGetPushSettings {
ApiKey = apiKey,
Source = apiUrl
});
}
})
.OnError(exception =>
{
Information("Publish-NuGet Task failed, but continuing with next Task...");
});
///////////////////////////////////////////////////////////////////////////////
// TARGETS
///////////////////////////////////////////////////////////////////////////////
@ -333,13 +262,24 @@ Task("Package")
Task("AppVeyor")
.IsDependentOn("Package")
.IsDependentOn("Copy-Files-Impl")
.IsDependentOn("Zip-Files-Impl")
.IsDependentOn("Publish-MyGet-Impl")
.IsDependentOn("Publish-NuGet-Impl");
.IsDependentOn("Zip-Files-Impl");
Task("Travis")
.IsDependentOn("Run-Tests");
Task("Azure-Linux")
.IsDependentOn("Run-Tests");
Task("Azure-OSX")
.IsDependentOn("Run-Tests")
.IsDependentOn("Copy-Files-Impl")
.IsDependentOn("Zip-Files-Impl");
Task("Azure-Windows")
.IsDependentOn("Package")
.IsDependentOn("Copy-Files-Impl")
.IsDependentOn("Zip-Files-Impl");
///////////////////////////////////////////////////////////////////////////////
// EXECUTE
///////////////////////////////////////////////////////////////////////////////

5
build/MonoMac.props

@ -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>

4
build/SharedVersion.props

@ -2,8 +2,8 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Product>Avalonia</Product>
<Version>0.6.2</Version>
<Copyright>Copyright 2016 &#169; The AvaloniaUI Project</Copyright>
<Version>0.7.0</Version>
<Copyright>Copyright 2018 &#169; The AvaloniaUI Project</Copyright>
<PackageLicenseUrl>https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/AvaloniaUI/Avalonia/</PackageProjectUrl>
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>

37
packages.cake

@ -150,7 +150,7 @@ public class Packages
nuspec.Symbols = false;
nuspec.NoPackageAnalysis = true;
nuspec.Description = "The Avalonia UI framework";
nuspec.Copyright = "Copyright 2015";
nuspec.Copyright = "Copyright 2018";
nuspec.Tags = new [] { "Avalonia" };
});
@ -408,20 +408,6 @@ public class Packages
BasePath = context.Directory("./src/Skia/Avalonia.Skia/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
},
new NuGetPackSettings()
{
Id = "Avalonia.MonoMac",
Dependencies = new DependencyBuilder(this)
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }
}.Dep("MonoMac.NetStandard").ToArray(),
Files = new []
{
new NuSpecContent { Source = "netstandard2.0/Avalonia.MonoMac.dll", Target = "lib/netstandard2.0" },
},
BasePath = context.Directory("./src/OSX/Avalonia.MonoMac/bin/" + parameters.DirSuffix),
OutputDirectory = parameters.NugetRoot
},
///////////////////////////////////////////////////////////////////////////////
// Avalonia.Desktop
///////////////////////////////////////////////////////////////////////////////
@ -434,7 +420,7 @@ public class Packages
new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.MonoMac", Version = parameters.Version }
new NuSpecDependency() { Id = "Avalonia.Native", Version = parameters.Version }
},
Files = new NuSpecContent[]
{
@ -460,7 +446,24 @@ public class Packages
},
BasePath = context.Directory("./src/Linux/"),
OutputDirectory = parameters.NugetRoot
}
},
///////////////////////////////////////////////////////////////////////////////
// Avalonia.Native
///////////////////////////////////////////////////////////////////////////////
new NuGetPackSettings()
{
Id = "Avalonia.Native",
Dependencies = new []
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.Native.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Avalonia.Native/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
},
};
var nuspecNuGetSettingInterop = new NuGetPackSettings()

8
parameters.cake

@ -14,6 +14,7 @@ public class Parameters
public bool IsRunningOnUnix { get; private set; }
public bool IsRunningOnWindows { get; private set; }
public bool IsRunningOnAppVeyor { get; private set; }
public bool IsRunningOnAzure { get; private set; }
public bool IsPullRequest { get; private set; }
public bool IsMainRepo { get; private set; }
public bool IsMasterBranch { get; private set; }
@ -53,6 +54,8 @@ public class Parameters
IsRunningOnUnix = context.IsRunningOnUnix();
IsRunningOnWindows = context.IsRunningOnWindows();
IsRunningOnAppVeyor = buildSystem.AppVeyor.IsRunningOnAppVeyor;
IsRunningOnAzure = buildSystem.IsRunningOnVSTS || buildSystem.IsRunningOnTFS || context.EnvironmentVariable("LOGNAME") == "vsts";
IsPullRequest = buildSystem.AppVeyor.Environment.PullRequest.IsPullRequest;
IsMainRepo = StringComparer.OrdinalIgnoreCase.Equals(MainRepo, buildSystem.AppVeyor.Environment.Repository.Name);
IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch, buildSystem.AppVeyor.Environment.Repository.Branch);
@ -85,6 +88,11 @@ public class Parameters
Version += "-build" + context.EnvironmentVariable("APPVEYOR_BUILD_NUMBER") + "-beta";
}
}
else if (IsRunningOnAzure)
{
// Use AssemblyVersion with Build as version
Version += "-build" + context.EnvironmentVariable("BUILD_BUILDID") + "-beta";
}
// DIRECTORIES
ArtifactsDir = (DirectoryPath)context.Directory("./artifacts");

6
readme.md

@ -2,9 +2,9 @@
# Avalonia
| Gitter Chat | Windows Build Status | Linux/Mac Build Status | Open Collective |
|---|---|---|---|
| [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build status](https://ci.appveyor.com/api/projects/status/hubk3k0w9idyibfg/branch/master?svg=true)](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [![Build Status](https://travis-ci.org/AvaloniaUI/Avalonia.svg?branch=master)](https://travis-ci.org/AvaloniaUI/Avalonia) | [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) |
| Gitter Chat | Build Status | Windows Build Status | Linux/Mac Build Status | Open Collective |
|---|---|---|---|---|
| [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) | [![Build status](https://ci.appveyor.com/api/projects/status/hubk3k0w9idyibfg/branch/master?svg=true)](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [![Build Status](https://travis-ci.org/AvaloniaUI/Avalonia.svg?branch=master)](https://travis-ci.org/AvaloniaUI/Avalonia) | [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) |
## About

4
src/Avalonia.DotNetCoreRuntime/AppBuilder.cs

@ -43,7 +43,7 @@ namespace Avalonia
if (os == OperatingSystemType.WinNT)
LoadWin32();
else if(os==OperatingSystemType.OSX)
LoadMonoMac();
LoadAvaloniaNative();
else
LoadGtk3();
this.UseSkia();
@ -51,7 +51,7 @@ namespace Avalonia
return this;
}
void LoadMonoMac() => this.UseMonoMac();
void LoadAvaloniaNative() => this.UseAvaloniaNative();
void LoadWin32() => this.UseWin32();
void LoadGtk3() => this.UseGtk3();
}

2
src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj

@ -8,8 +8,8 @@
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj" />
<ProjectReference Include="..\OSX\Avalonia.MonoMac\Avalonia.MonoMac.csproj" />
<ProjectReference Include="..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
<ProjectReference Include="..\Avalonia.Native\Avalonia.Native.csproj" />
<ProjectReference Include="..\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
<ProjectReference Include="..\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
</ItemGroup>

9
src/Avalonia.Native/Avalonia.Native.csproj

@ -19,4 +19,13 @@
<ProjectReference Include="..\Avalonia.OpenGL\Avalonia.OpenGL.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release' AND '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
<Content Include="../../Build/Products/Release/libAvalonia.Native.OSX.dylib">
<PackagePath>runtimes/osx/native/libAvaloniaNative.dylib</PackagePath>
<Pack>true</Pack>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

6
src/OSX/Avalonia.MonoMac/AssemblyInfo.cs

@ -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))]

20
src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj

@ -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>

29
src/OSX/Avalonia.MonoMac/ClipboardImpl.cs

@ -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;
}
}
}

66
src/OSX/Avalonia.MonoMac/Cursor.cs

@ -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]);
}
}
}

119
src/OSX/Avalonia.MonoMac/DragSource.cs

@ -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();
}
}
}

89
src/OSX/Avalonia.MonoMac/DraggingInfo.cs

@ -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();
}
}
}

106
src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs

@ -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;
}
}
}

40
src/OSX/Avalonia.MonoMac/Helpers.cs

@ -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);
}
}
}

267
src/OSX/Avalonia.MonoMac/KeyTransform.cs

@ -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;
}
}
}

25
src/OSX/Avalonia.MonoMac/MacScreen.cs

@ -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);
}
}
}

140
src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs

@ -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");
}
}
}

74
src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs

@ -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();
}
}
}
}
}

30
src/OSX/Avalonia.MonoMac/PopupImpl.cs

@ -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;
}
}
}

28
src/OSX/Avalonia.MonoMac/RenderTimer.cs

@ -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));
}
});
}
}
}

49
src/OSX/Avalonia.MonoMac/ScreenImpl.cs

@ -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;
}
}
}

47
src/OSX/Avalonia.MonoMac/Stubs.cs

@ -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);
}
}
}

89
src/OSX/Avalonia.MonoMac/SystemDialogsImpl.cs

@ -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();
}
}
}

505
src/OSX/Avalonia.MonoMac/TopLevelImpl.cs

@ -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);
}
}

208
src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs

@ -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();
}
}
}

176
src/OSX/Avalonia.MonoMac/WindowImpl.cs

@ -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…
Cancel
Save