Browse Source

Merge branch 'master' into fixed-property-equals-selector

pull/5150/head
Yoh Deadfall 5 years ago
committed by GitHub
parent
commit
c7944a075d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 8
      .github/ISSUE_TEMPLATE/config.yml
  3. 19
      .github/ISSUE_TEMPLATE/feature_request.md
  4. 4
      .github/PULL_REQUEST_TEMPLATE.md
  5. 4
      .ncrunch/Avalonia.MicroCom.v3.ncrunchproject
  6. 3
      .ncrunch/Avalonia.Win32.v3.ncrunchproject
  7. 1
      .ncrunch/Direct3DInteropSample.v3.ncrunchproject
  8. 2
      Avalonia.sln
  9. 3
      Avalonia.sln.DotSettings
  10. 22
      Documentation/build.md
  11. 6
      azure-pipelines.yml
  12. 2
      build/AndroidWorkarounds.props
  13. 2
      build/ApiDiff.props
  14. 4
      build/HarfBuzzSharp.props
  15. 2
      build/ReactiveUI.props
  16. 4
      build/SharedVersion.props
  17. 23
      build/SourceLink.props
  18. 1020
      native/Avalonia.Native/inc/key.h
  19. 12
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  20. 1
      native/Avalonia.Native/src/OSX/AvnString.h
  21. 15
      native/Avalonia.Native/src/OSX/AvnString.mm
  22. 7
      native/Avalonia.Native/src/OSX/KeyTransform.h
  23. 151
      native/Avalonia.Native/src/OSX/KeyTransform.mm
  24. 10
      native/Avalonia.Native/src/OSX/Screens.mm
  25. 28
      native/Avalonia.Native/src/OSX/app.mm
  26. 2
      native/Avalonia.Native/src/OSX/clipboard.mm
  27. 7
      native/Avalonia.Native/src/OSX/common.h
  28. 22
      native/Avalonia.Native/src/OSX/cursor.mm
  29. 35
      native/Avalonia.Native/src/OSX/main.mm
  30. 11
      native/Avalonia.Native/src/OSX/menu.h
  31. 182
      native/Avalonia.Native/src/OSX/menu.mm
  32. 12
      native/Avalonia.Native/src/OSX/rendertarget.mm
  33. 1
      native/Avalonia.Native/src/OSX/window.h
  34. 72
      native/Avalonia.Native/src/OSX/window.mm
  35. 58
      nukebuild/Build.cs
  36. 7
      nukebuild/BuildParameters.cs
  37. 1
      nukebuild/_build.csproj
  38. 12
      packages/Avalonia/Avalonia.csproj
  39. 13
      readme.md
  40. 14
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  41. 21
      samples/ControlCatalog.Android/MainActivity.cs
  42. 2
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  43. 59
      samples/ControlCatalog.Android/Resources/Resource.Designer.cs
  44. BIN
      samples/ControlCatalog.Android/Resources/drawable/Icon.png
  45. 13
      samples/ControlCatalog.Android/Resources/drawable/splash_screen.xml
  46. 13
      samples/ControlCatalog.Android/Resources/layout/Main.axml
  47. 5
      samples/ControlCatalog.Android/Resources/values/Strings.xml
  48. 4
      samples/ControlCatalog.Android/Resources/values/colors.xml
  49. 17
      samples/ControlCatalog.Android/Resources/values/styles.xml
  50. 32
      samples/ControlCatalog.Android/SplashActivity.cs
  51. 6
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  52. 6
      samples/ControlCatalog.NetCore/Program.cs
  53. 8
      samples/ControlCatalog/App.xaml.cs
  54. BIN
      samples/ControlCatalog/Assets/Fonts/WenQuanYiMicroHei-01.ttf
  55. BIN
      samples/ControlCatalog/Assets/avalonia-32.png
  56. 2
      samples/ControlCatalog/ControlCatalog.csproj
  57. 10
      samples/ControlCatalog/MainView.xaml
  58. 4
      samples/ControlCatalog/MainWindow.xaml
  59. 8
      samples/ControlCatalog/Pages/AcrylicPage.xaml
  60. 102
      samples/ControlCatalog/Pages/ContextFlyoutPage.axaml
  61. 45
      samples/ControlCatalog/Pages/ContextFlyoutPage.axaml.cs
  62. 1
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  63. 29
      samples/ControlCatalog/Pages/CursorPage.xaml
  64. 20
      samples/ControlCatalog/Pages/CursorPage.xaml.cs
  65. 9
      samples/ControlCatalog/Pages/DataGridPage.xaml
  66. 6
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  67. 264
      samples/ControlCatalog/Pages/FlyoutsPage.axaml
  68. 81
      samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs
  69. 7
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  70. 16
      samples/ControlCatalog/Pages/ScreenPage.cs
  71. 29
      samples/ControlCatalog/Pages/SliderPage.xaml
  72. 5
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  73. 6
      samples/ControlCatalog/Pages/TextBoxPage.xaml.cs
  74. 27
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  75. 76
      samples/ControlCatalog/Pages/ViewboxPage.xaml
  76. 21
      samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
  77. 78
      samples/ControlCatalog/ViewModels/ContextFlyoutPageViewModel.cs
  78. 44
      samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
  79. 3
      samples/RenderDemo/MainWindow.xaml
  80. 7
      samples/RenderDemo/Pages/AnimationsPage.xaml
  81. 89
      samples/RenderDemo/Pages/PathMeasurementPage.cs
  82. 65
      samples/RenderDemo/Pages/TransitionsPage.xaml
  83. 47
      src/Android/Avalonia.Android/ActivityTracker.cs
  84. 96
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  85. 45
      src/Android/Avalonia.Android/AndroidPlatform.cs
  86. 15
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  87. 2
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  88. 11
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  89. 35
      src/Android/Avalonia.Android/AvaloniaView.cs
  90. 101
      src/Android/Avalonia.Android/ChoreographerTimer.cs
  91. 17
      src/Android/Avalonia.Android/CursorFactory.cs
  92. 12
      src/Android/Avalonia.Android/IInitEditorInfo.cs
  93. 30
      src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
  94. 30
      src/Android/Avalonia.Android/OpenGL/GlRenderTarget.cs
  95. 4
      src/Android/Avalonia.Android/Platform/ClipboardImpl.cs
  96. 14
      src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs
  97. 6
      src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
  98. 17
      src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs
  99. 4
      src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs
  100. 127
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

32
.github/ISSUE_TEMPLATE/bug_report.md

@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve Avalonia
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows, Mac, Linux (State distribution)]
- Version [e.g. 0.10.0-rc1 or 0.9.12]
**Additional context**
Add any other context about the problem here.

8
.github/ISSUE_TEMPLATE/config.yml

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Questions, Discussions, Ideas
url: https://github.com/AvaloniaUI/Avalonia/discussions/new
about: Please ask and answer questions here.
- name: Avalonia Community Support on Gitter
url: https://gitter.im/AvaloniaUI/Avalonia
about: Please ask and answer questions here.

19
.github/ISSUE_TEMPLATE/feature_request.md

@ -0,0 +1,19 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

4
.github/PULL_REQUEST_TEMPLATE.md

@ -18,11 +18,13 @@
- [ ] Added unit tests (if possible)? - [ ] Added unit tests (if possible)?
- [ ] Added XML documentation to any related classes? - [ ] Added XML documentation to any related classes?
- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Avaloniaui.net with user documentation - [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Documentation with user documentation
## Breaking changes ## Breaking changes
<!--- List any breaking changes here. When the PR is merged please add an entry to https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes --> <!--- List any breaking changes here. When the PR is merged please add an entry to https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes -->
## Obsoletions / Deprecations
<!--- Obsolete and Deprecated attributes on APIs MUST only be included when discussed with Core team. @grokys, @kekekeks & @danwalmsley -->
## Fixed issues ## Fixed issues
<!--- If the pull request fixes issue(s) list them like this: <!--- If the pull request fixes issue(s) list them like this:

4
.ncrunch/Avalonia.MicroCom.v3.ncrunchproject

@ -1,5 +1,3 @@
<ProjectConfiguration> <ProjectConfiguration>
<Settings> <Settings />
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration> </ProjectConfiguration>

3
.ncrunch/Avalonia.Win32.v3.ncrunchproject

@ -1,5 +1,8 @@
<ProjectConfiguration> <ProjectConfiguration>
<Settings> <Settings>
<AdditionalFilesToIncludeForProject>
<Value>..\..\tools\MicroComGenerator\bin\Debug\netcoreapp3.1\**.*</Value>
</AdditionalFilesToIncludeForProject>
<HiddenComponentWarnings> <HiddenComponentWarnings>
<Value>MissingOrIgnoredProjectReference</Value> <Value>MissingOrIgnoredProjectReference</Value>
</HiddenComponentWarnings> </HiddenComponentWarnings>

1
.ncrunch/Direct3DInteropSample.v3.ncrunchproject

@ -3,6 +3,7 @@
<HiddenComponentWarnings> <HiddenComponentWarnings>
<Value>MissingOrIgnoredProjectReference</Value> <Value>MissingOrIgnoredProjectReference</Value>
</HiddenComponentWarnings> </HiddenComponentWarnings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully> <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings> </Settings>
</ProjectConfiguration> </ProjectConfiguration>

2
Avalonia.sln

@ -222,8 +222,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "sr
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.Events", "src\Avalonia.ReactiveUI.Events\Avalonia.ReactiveUI.Events.csproj", "{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sandbox", "samples\Sandbox\Sandbox.csproj", "{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sandbox", "samples\Sandbox\Sandbox.csproj", "{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroComGenerator", "src\tools\MicroComGenerator\MicroComGenerator.csproj", "{AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroComGenerator", "src\tools\MicroComGenerator\MicroComGenerator.csproj", "{AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}"

3
Avalonia.sln.DotSettings

@ -38,4 +38,5 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002EDaemon_002ESettings_002EMigration_002ESwaWarningsModeSettingsMigrate/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002EDaemon_002ESettings_002EMigration_002ESwaWarningsModeSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Avalonia/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=Avalonia/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fcitx/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

22
Documentation/build.md

@ -9,10 +9,24 @@ git clone https://github.com/AvaloniaUI/Avalonia.git
git submodule update --init git submodule update --init
``` ```
### Install the required version of the .NET Core SDK
Go to https://dotnet.microsoft.com/download/visual-studio-sdks and install the latest version of the .NET Core SDK compatible with Avalonia UI. Make sure to download the SDK (not just the "runtime") package. The version compatible is indicated within the [global.json](https://github.com/AvaloniaUI/Avalonia/blob/master/global.json) file. Note that Avalonia UI does not always use the latest version and is hardcoded to use the last version known to be compatible (SDK releases may break the builds from time-to-time).
### Open in Visual Studio ### Open in Visual Studio
Open the `Avalonia.sln` solution in Visual Studio 2019 or newer. The free Visual Studio Community Open the `Avalonia.sln` solution in Visual Studio 2019 or newer. The free Visual Studio Community edition works fine. Build and run the `Samples\ControlCatalog.Desktop` or `ControlCatalog.NetCore` project to see the sample application.
edition works fine. Run the `Samples\ControlCatalog.Desktop` project to see the sample application.
### Troubleshooting
* **Error CS0006: Avalonia.DesktopRuntime.dll could not be found**
It is common for the first build to fail with the errors below (also discussed in [#4257](https://github.com/AvaloniaUI/Avalonia/issues/4257)).
```
>CSC : error CS0006: Metadata file 'C:\...\Avalonia\src\Avalonia.DesktopRuntime\bin\Debug\netcoreapp2.0\Avalonia.DesktopRuntime.dll' could not be found
>CSC : error CS0006: Metadata file 'C:\...\Avalonia\packages\Avalonia\bin\Debug\netcoreapp2.0\Avalonia.dll' could not be found
```
To correct this, right click on the `Avalonia.DesktopRuntime` project then press `Build` to build the project manually. Afterwards the solution should build normally and the ControlCatalog can be run.
# Linux/macOS # Linux/macOS
@ -20,9 +34,9 @@ It's *not* possible to build the *whole* project on Linux/macOS. You can only bu
MonoDevelop, Xamarin Studio and Visual Studio for Mac aren't capable of properly opening our solution. You can use Rider (at least 2017.2 EAP) or VSCode instead. They will fail to load most of platform specific projects, but you don't need them to run on .NET Core. MonoDevelop, Xamarin Studio and Visual Studio for Mac aren't capable of properly opening our solution. You can use Rider (at least 2017.2 EAP) or VSCode instead. They will fail to load most of platform specific projects, but you don't need them to run on .NET Core.
### Install the latest version of .NET Core ### Install the latest version of the .NET Core SDK
Go to https://www.microsoft.com/net/core and follow instructions for your OS. You need SDK (not just "runtime") package. Go to https://www.microsoft.com/net/core and follow the instructions for your OS. Make sure to download the SDK (not just the "runtime") package.
### Additional requirements for macOS ### Additional requirements for macOS

6
azure-pipelines.yml

@ -27,7 +27,7 @@ jobs:
variables: variables:
SolutionDir: '$(Build.SourcesDirectory)' SolutionDir: '$(Build.SourcesDirectory)'
pool: pool:
vmImage: 'macOS-10.14' vmImage: 'macOS-10.15'
steps: steps:
- task: UseDotNet@2 - task: UseDotNet@2
displayName: 'Use .NET Core SDK 3.1.401' displayName: 'Use .NET Core SDK 3.1.401'
@ -51,10 +51,10 @@ jobs:
inputs: inputs:
actions: 'build' actions: 'build'
scheme: '' scheme: ''
sdk: 'macosx10.14' sdk: 'macosx11.1'
configuration: 'Release' configuration: 'Release'
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
xcodeVersion: '10' # Options: 8, 9, default, specifyPath xcodeVersion: '12' # Options: 8, 9, default, specifyPath
args: '-derivedDataPath ./' args: '-derivedDataPath ./'
- task: CmdLine@2 - task: CmdLine@2

2
build/AndroidWorkarounds.props

@ -2,7 +2,7 @@
<ItemGroup Condition="'$(AndroidApplication)' == 'true'"> <ItemGroup Condition="'$(AndroidApplication)' == 'true'">
<!-- WORKAROUND: The packages below are transitively referenced by System.Memory, <!-- WORKAROUND: The packages below are transitively referenced by System.Memory,
but Xamarin.Android applications need the newest versions directly referenced for the linker. --> but Xamarin.Android applications need the newest versions directly referenced for the linker. -->
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" /> <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
<PackageReference Include="System.Buffers" Version="4.5.0" /> <PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<Target Name="_RemoveNonExistingResgenFile" BeforeTargets="CoreCompile" Condition="'$(_SdkSetAndroidResgenFile)' == 'true' And '$(AndroidResgenFile)' != '' And !Exists('$(AndroidResgenFile)')"> <Target Name="_RemoveNonExistingResgenFile" BeforeTargets="CoreCompile" Condition="'$(_SdkSetAndroidResgenFile)' == 'true' And '$(AndroidResgenFile)' != '' And !Exists('$(AndroidResgenFile)')">

2
build/ApiDiff.props

@ -1,6 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<ApiContractPackageVersion>0.10.0-preview6</ApiContractPackageVersion> <ApiContractPackageVersion>0.10.0</ApiContractPackageVersion>
<NugetPackageName Condition="'$(PackageId)' != ''">$(PackageId)</NugetPackageName> <NugetPackageName Condition="'$(PackageId)' != ''">$(PackageId)</NugetPackageName>
<NugetPackageName Condition="'$(PackageId)' == ''">Avalonia</NugetPackageName> <NugetPackageName Condition="'$(PackageId)' == ''">Avalonia</NugetPackageName>
</PropertyGroup> </PropertyGroup>

4
build/HarfBuzzSharp.props

@ -1,6 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="HarfBuzzSharp" Version="2.6.1.6" /> <PackageReference Include="HarfBuzzSharp" Version="2.6.1.7" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.6.1.6" /> <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.6.1.7" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
build/ReactiveUI.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="ReactiveUI" Version="12.1.1" /> <PackageReference Include="ReactiveUI" Version="13.2.10" />
</ItemGroup> </ItemGroup>
</Project> </Project>

4
build/SharedVersion.props

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<Product>Avalonia</Product> <Product>Avalonia</Product>
<Version>0.10.999</Version> <Version>0.10.999</Version>
<Copyright>Copyright 2020 &#169; The AvaloniaUI Project</Copyright> <Copyright>Copyright 2021 &#169; The AvaloniaUI Project</Copyright>
<PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl> <PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl>
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl> <RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
@ -21,6 +21,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="PackageIcon"> <ItemGroup Label="PackageIcon">
<None Include="$(MSBuildThisFileDirectory)/Assets/Icon.png" Pack="true" PackagePath=""/> <None Include="$(MSBuildThisFileDirectory)/Assets/Icon.png" Pack="true" Visible="false" PackagePath=""/>
</ItemGroup> </ItemGroup>
</Project> </Project>

23
build/SourceLink.props

@ -1,5 +1,26 @@
<Project> <Project>
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>false</IncludeSymbols>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<DebugType>embedded</DebugType>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>
<!-- Workaround for https://github.com/dotnet/sdk/issues/11105 -->
<ItemGroup>
<SourceRoot Include="$(NuGetPackageRoot)" Condition="'$(NuGetPackageRoot)' != ''" />
</ItemGroup> </ItemGroup>
</Project> </Project>

1020
native/Avalonia.Native/inc/key.h

File diff suppressed because it is too large

12
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -8,19 +8,20 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; };
1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; };
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; };
1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; }; 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; };
1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; };
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; }; 1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; }; 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; }; 1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; };
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; }; 37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; }; 37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; }; 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; }; 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; };
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; }; 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; };
520624B322973F4100C4DCEF /* menu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 520624B222973F4100C4DCEF /* menu.mm */; }; 520624B322973F4100C4DCEF /* menu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 520624B222973F4100C4DCEF /* menu.mm */; };
522D5959258159C1006F7F7A /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 522D5958258159C1006F7F7A /* Carbon.framework */; };
5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; }; 5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; }; 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
@ -32,13 +33,13 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = "<group>"; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = "<group>"; };
1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = "<group>"; };
1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = "<group>"; }; 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = "<group>"; };
1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; };
1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = "<group>"; };
1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = "<group>"; };
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; }; 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; };
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; }; 1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; };
1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = "<group>"; };
37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; }; 37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; };
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; }; 379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; }; 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; };
@ -49,6 +50,7 @@
37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = "<group>"; }; 37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = "<group>"; };
37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; }; 37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; };
520624B222973F4100C4DCEF /* menu.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = menu.mm; sourceTree = "<group>"; }; 520624B222973F4100C4DCEF /* menu.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = menu.mm; sourceTree = "<group>"; };
522D5958258159C1006F7F7A /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = "<group>"; }; 5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = "<group>"; };
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; }; 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; };
5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; }; 5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
@ -69,6 +71,7 @@
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */, 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */,
1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */, 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */,
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */, AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */,
522D5959258159C1006F7F7A /* Carbon.framework in Frameworks */,
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */, AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -79,6 +82,7 @@
AB661C1C2148230E00291242 /* Frameworks */ = { AB661C1C2148230E00291242 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
522D5958258159C1006F7F7A /* Carbon.framework */,
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */, 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */,
1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */, 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */,
AB1E522B217613570091CD71 /* OpenGL.framework */, AB1E522B217613570091CD71 /* OpenGL.framework */,

1
native/Avalonia.Native/src/OSX/AvnString.h

@ -11,6 +11,7 @@
extern IAvnString* CreateAvnString(NSString* string); extern IAvnString* CreateAvnString(NSString* string);
extern IAvnStringArray* CreateAvnStringArray(NSArray<NSString*>* array); extern IAvnStringArray* CreateAvnStringArray(NSArray<NSString*>* array);
extern IAvnStringArray* CreateAvnStringArray(NSArray<NSURL*>* array);
extern IAvnStringArray* CreateAvnStringArray(NSString* string); extern IAvnStringArray* CreateAvnStringArray(NSString* string);
extern IAvnString* CreateByteArray(void* data, int len); extern IAvnString* CreateByteArray(void* data, int len);
#endif /* AvnString_h */ #endif /* AvnString_h */

15
native/Avalonia.Native/src/OSX/AvnString.mm

@ -85,6 +85,16 @@ public:
} }
} }
AvnStringArrayImpl(NSArray<NSURL*>* array)
{
for(int c = 0; c < [array count]; c++)
{
ComPtr<IAvnString> s;
*s.getPPV() = new AvnStringImpl([array objectAtIndex:c].absoluteString);
_list.push_back(s);
}
}
AvnStringArrayImpl(NSString* string) AvnStringArrayImpl(NSString* string)
{ {
ComPtr<IAvnString> s; ComPtr<IAvnString> s;
@ -117,6 +127,11 @@ IAvnStringArray* CreateAvnStringArray(NSArray<NSString*> * array)
return new AvnStringArrayImpl(array); return new AvnStringArrayImpl(array);
} }
IAvnStringArray* CreateAvnStringArray(NSArray<NSURL*> * array)
{
return new AvnStringArrayImpl(array);
}
IAvnStringArray* CreateAvnStringArray(NSString* string) IAvnStringArray* CreateAvnStringArray(NSString* string)
{ {
return new AvnStringArrayImpl(string); return new AvnStringArrayImpl(string);

7
native/Avalonia.Native/src/OSX/KeyTransform.h

@ -1,9 +1,14 @@
#ifndef keytransform_h #ifndef keytransform_h
#define keytransform_h #define keytransform_h
#include "common.h" #include "common.h"
#include "key.h"
#include <map> #include <map>
extern std::map<int, AvnKey> s_KeyMap; extern std::map<int, AvnKey> s_KeyMap;
extern std::map<AvnKey, int> s_AvnKeyMap;
extern std::map<int, const char*> s_QwertyKeyMap;
extern std::map<AvnKey, int> s_UnicodeKeyMap;
#endif #endif

151
native/Avalonia.Native/src/OSX/KeyTransform.mm

@ -120,6 +120,138 @@ const int kVK_UpArrow = 0x7E;
//const int kVK_JIS_Eisu = 0x66; //const int kVK_JIS_Eisu = 0x66;
const int kVK_JIS_Kana = 0x68; const int kVK_JIS_Kana = 0x68;
// converts from AvaloniaKeys to UnicodeSpecial keys.
std::map<AvnKey, int> s_UnicodeKeyMap =
{
{ Up, NSUpArrowFunctionKey },
{ Down, NSDownArrowFunctionKey },
{ Left, NSLeftArrowFunctionKey },
{ Right, NSRightArrowFunctionKey },
{ F1, NSF1FunctionKey },
{ F2, NSF2FunctionKey },
{ F3, NSF3FunctionKey },
{ F4, NSF4FunctionKey },
{ F5, NSF5FunctionKey },
{ F6, NSF6FunctionKey },
{ F7, NSF7FunctionKey },
{ F8, NSF8FunctionKey },
{ F9, NSF9FunctionKey },
{ F10, NSF10FunctionKey },
{ F11, NSF11FunctionKey },
{ F12, NSF12FunctionKey },
{ F13, NSF13FunctionKey },
{ F14, NSF14FunctionKey },
{ F15, NSF15FunctionKey },
{ F16, NSF16FunctionKey },
{ F17, NSF17FunctionKey },
{ F18, NSF18FunctionKey },
{ F19, NSF19FunctionKey },
{ F20, NSF20FunctionKey },
{ F21, NSF21FunctionKey },
{ F22, NSF22FunctionKey },
{ F23, NSF23FunctionKey },
{ F24, NSF24FunctionKey },
{ Insert, NSInsertFunctionKey },
{ Delete, NSDeleteFunctionKey },
{ Home, NSHomeFunctionKey },
//{ Begin, NSBeginFunctionKey },
{ End, NSEndFunctionKey },
{ PageUp, NSPageUpFunctionKey },
{ PageDown, NSPageDownFunctionKey },
{ PrintScreen, NSPrintScreenFunctionKey },
{ Scroll, NSScrollLockFunctionKey },
//{ SysReq, NSSysReqFunctionKey },
//{ Break, NSBreakFunctionKey },
//{ Reset, NSResetFunctionKey },
//{ Stop, NSStopFunctionKey },
//{ Menu, NSMenuFunctionKey },
//{ UserFunction, NSUserFunctionKey },
//{ SystemFunction, NSSystemFunctionKey },
{ Print, NSPrintFunctionKey },
//{ ClearLine, NSClearLineFunctionKey },
//{ ClearDisplay, NSClearDisplayFunctionKey },
};
// Converts from Ansi virtual keys to Qwerty Keyboard map.
std::map<int, const char*> s_QwertyKeyMap =
{
{ 0, "a" },
{ 1, "s" },
{ 2, "d" },
{ 3, "f" },
{ 4, "h" },
{ 5, "g" },
{ 6, "z" },
{ 7, "x" },
{ 8, "c" },
{ 9, "v" },
{ 10, "§" },
{ 11, "b" },
{ 12, "q" },
{ 13, "w" },
{ 14, "e" },
{ 15, "r" },
{ 16, "y" },
{ 17, "t" },
{ 18, "1" },
{ 19, "2" },
{ 20, "3" },
{ 21, "4" },
{ 22, "6" },
{ 23, "5" },
{ 24, "=" },
{ 25, "9" },
{ 26, "7" },
{ 27, "-" },
{ 28, "8" },
{ 29, "0" },
{ 30, "]" },
{ 31, "o" },
{ 32, "u" },
{ 33, "[" },
{ 34, "i" },
{ 35, "p" },
{ 37, "l" },
{ 38, "j" },
{ 39, "'" },
{ 40, "k" },
{ 41, ";" },
{ 42, "\\" },
{ 43, "," },
{ 44, "/" },
{ 45, "n" },
{ 46, "m" },
{ 47, "." },
{ 49, " " },
{ 50, "`" },
{ 51, "" },
{ 52, "" },
{ 53, "" },
{ 65, "." },
{ 66, "" },
{ 67, "*" },
{ 69, "+" },
{ 70, "" },
{ 71, "" },
{ 72, "" },
{ 75, "/" },
{ 76, "" },
{ 77, "" },
{ 78, "-" },
{ 81, "=" },
{ 82, "0" },
{ 83, "1" },
{ 84, "2" },
{ 85, "3" },
{ 86, "4" },
{ 87, "5" },
{ 88, "6" },
{ 89, "7" },
{ 91, "8" },
{ 92, "9" }
};
// converts from ansi virtualkeys to AvnKeys.
std::map<int, AvnKey> s_KeyMap = std::map<int, AvnKey> s_KeyMap =
{ {
{kVK_ANSI_A, A}, {kVK_ANSI_A, A},
@ -237,3 +369,22 @@ const int kVK_JIS_Kana = 0x68;
{kVK_UpArrow, Up}, {kVK_UpArrow, Up},
{kVK_JIS_Kana, AvnKeyKanaMode}, {kVK_JIS_Kana, AvnKeyKanaMode},
}; };
static std::map<AvnKey, int> BuildAvnKeyMap ()
{
std::map<AvnKey, int> result;
for( auto it = s_KeyMap.begin(); it != s_KeyMap.end(); ++it )
{
int key = it->first;
AvnKey value = it->second;
result[value] = key;
}
return result;
}
// Converts AvnKeys to Ansi VirtualKeys
std::map<AvnKey, int> s_AvnKeyMap = BuildAvnKeyMap();

10
native/Avalonia.Native/src/OSX/Screens.mm

@ -5,12 +5,6 @@ class Screens : public ComSingleObject<IAvnScreens, &IID_IAvnScreens>
public: public:
FORWARD_IUNKNOWN() FORWARD_IUNKNOWN()
private:
CGFloat PrimaryDisplayHeight()
{
return NSMaxY([[[NSScreen screens] firstObject] frame]);
}
public: public:
virtual HRESULT GetScreenCount (int* ret) override virtual HRESULT GetScreenCount (int* ret) override
{ {
@ -36,12 +30,12 @@ public:
ret->Bounds.Height = [screen frame].size.height; ret->Bounds.Height = [screen frame].size.height;
ret->Bounds.Width = [screen frame].size.width; ret->Bounds.Width = [screen frame].size.width;
ret->Bounds.X = [screen frame].origin.x; ret->Bounds.X = [screen frame].origin.x;
ret->Bounds.Y = PrimaryDisplayHeight() - [screen frame].origin.y - ret->Bounds.Height; ret->Bounds.Y = ConvertPointY(ToAvnPoint([screen frame].origin)).Y - ret->Bounds.Height;
ret->WorkingArea.Height = [screen visibleFrame].size.height; ret->WorkingArea.Height = [screen visibleFrame].size.height;
ret->WorkingArea.Width = [screen visibleFrame].size.width; ret->WorkingArea.Width = [screen visibleFrame].size.width;
ret->WorkingArea.X = [screen visibleFrame].origin.x; ret->WorkingArea.X = [screen visibleFrame].origin.x;
ret->WorkingArea.Y = ret->Bounds.Height - [screen visibleFrame].origin.y - ret->WorkingArea.Height; ret->WorkingArea.Y = ConvertPointY(ToAvnPoint([screen visibleFrame].origin)).Y - ret->WorkingArea.Height;
ret->PixelDensity = [screen backingScaleFactor]; ret->PixelDensity = [screen backingScaleFactor];

28
native/Avalonia.Native/src/OSX/app.mm

@ -1,10 +1,20 @@
#include "common.h" #include "common.h"
#include "AvnString.h"
@interface AvnAppDelegate : NSObject<NSApplicationDelegate> @interface AvnAppDelegate : NSObject<NSApplicationDelegate>
-(AvnAppDelegate* _Nonnull) initWithEvents: (IAvnApplicationEvents* _Nonnull) events;
@end @end
NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular; NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular;
@implementation AvnAppDelegate @implementation AvnAppDelegate
ComPtr<IAvnApplicationEvents> _events;
- (AvnAppDelegate *)initWithEvents:(IAvnApplicationEvents *)events
{
_events = events;
return self;
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification - (void)applicationWillFinishLaunching:(NSNotification *)notification
{ {
if([[NSApplication sharedApplication] activationPolicy] != AvnDesiredActivationPolicy) if([[NSApplication sharedApplication] activationPolicy] != AvnDesiredActivationPolicy)
@ -27,11 +37,23 @@ NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivati
[[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps]; [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
} }
- (void)application:(NSApplication *)sender openFiles:(NSArray<NSString *> *)filenames
{
auto array = CreateAvnStringArray(filenames);
_events->FilesOpened(array);
}
- (void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls
{
auto array = CreateAvnStringArray(urls);
_events->FilesOpened(array);
}
@end @end
@interface AvnApplication : NSApplication @interface AvnApplication : NSApplication
@end @end
@implementation AvnApplication @implementation AvnApplication
@ -63,9 +85,9 @@ NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivati
@end @end
extern void InitializeAvnApp() extern void InitializeAvnApp(IAvnApplicationEvents* events)
{ {
NSApplication* app = [AvnApplication sharedApplication]; NSApplication* app = [AvnApplication sharedApplication];
id delegate = [AvnAppDelegate new]; id delegate = [[AvnAppDelegate alloc] initWithEvents:events];
[app setDelegate:delegate]; [app setDelegate:delegate];
} }

2
native/Avalonia.Native/src/OSX/clipboard.mm

@ -56,7 +56,7 @@ public:
return S_OK; return S_OK;
} }
NSArray* arr = (NSArray*)data; NSArray<NSString*>* arr = (NSArray*)data;
for(int c = 0; c < [arr count]; c++) for(int c = 0; c < [arr count]; c++)
if(![[arr objectAtIndex:c] isKindOfClass:[NSString class]]) if(![[arr objectAtIndex:c] isKindOfClass:[NSString class]])

7
native/Avalonia.Native/src/OSX/common.h

@ -23,17 +23,20 @@ extern IAvnCursorFactory* CreateCursorFactory();
extern IAvnGlDisplay* GetGlDisplay(); extern IAvnGlDisplay* GetGlDisplay();
extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events); extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events);
extern IAvnMenuItem* CreateAppMenuItem(); extern IAvnMenuItem* CreateAppMenuItem();
extern IAvnMenuItem* CreateAppMenuItemSeperator(); extern IAvnMenuItem* CreateAppMenuItemSeparator();
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent); extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu); extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu);
extern IAvnMenu* GetAppMenu (); extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem (); extern NSMenuItem* GetAppMenuItem ();
extern void SetAutoGenerateDefaultAppMenuItems (bool enabled);
extern bool GetAutoGenerateDefaultAppMenuItems ();
extern void InitializeAvnApp(); extern void InitializeAvnApp(IAvnApplicationEvents* events);
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;
extern NSPoint ToNSPoint (AvnPoint p); extern NSPoint ToNSPoint (AvnPoint p);
extern AvnPoint ToAvnPoint (NSPoint p); extern AvnPoint ToAvnPoint (NSPoint p);
extern AvnPoint ConvertPointY (AvnPoint p); extern AvnPoint ConvertPointY (AvnPoint p);
extern CGFloat PrimaryDisplayHeight();
extern NSSize ToNSSize (AvnSize s); extern NSSize ToNSSize (AvnSize s);
#ifdef DEBUG #ifdef DEBUG
#define NSDebugLog(...) NSLog(__VA_ARGS__) #define NSDebugLog(...) NSLog(__VA_ARGS__)

22
native/Avalonia.Native/src/OSX/cursor.mm

@ -62,6 +62,28 @@ public:
return S_OK; return S_OK;
} }
virtual HRESULT CreateCustomCursor (void* bitmapData, size_t length, AvnPixelSize hotPixel, IAvnCursor** retOut) override
{
if(bitmapData == nullptr || retOut == nullptr)
{
return E_POINTER;
}
NSData *imageData = [NSData dataWithBytes:bitmapData length:length];
NSImage *image = [[NSImage alloc] initWithData:imageData];
NSPoint hotSpot;
hotSpot.x = hotPixel.Width;
hotSpot.y = hotPixel.Height;
*retOut = new Cursor([[NSCursor new] initWithImage: image hotSpot: hotSpot]);
(*retOut)->AddRef();
return S_OK;
}
}; };
extern IAvnCursorFactory* CreateCursorFactory() extern IAvnCursorFactory* CreateCursorFactory()

35
native/Avalonia.Native/src/OSX/main.mm

@ -2,6 +2,7 @@
#define COM_GUIDS_MATERIALIZE #define COM_GUIDS_MATERIALIZE
#include "common.h" #include "common.h"
static bool s_generateDefaultAppMenuItems = true;
static NSString* s_appTitle = @"Avalonia"; static NSString* s_appTitle = @"Avalonia";
// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
@ -122,6 +123,12 @@ public:
? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory; ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory;
return S_OK; return S_OK;
} }
virtual HRESULT SetDisableDefaultApplicationMenuItems (bool enabled) override
{
SetAutoGenerateDefaultAppMenuItems(!enabled);
return S_OK;
}
}; };
/// See "Using POSIX Threads in a Cocoa Application" section here: /// See "Using POSIX Threads in a Cocoa Application" section here:
@ -156,13 +163,13 @@ class AvaloniaNative : public ComSingleObject<IAvaloniaNativeFactory, &IID_IAval
public: public:
FORWARD_IUNKNOWN() FORWARD_IUNKNOWN()
virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator) override virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, IAvnApplicationEvents* events) override
{ {
_deallocator = deallocator; _deallocator = deallocator;
@autoreleasepool{ @autoreleasepool{
[[ThreadingInitializer new] do]; [[ThreadingInitializer new] do];
} }
InitializeAvnApp(); InitializeAvnApp(events);
return S_OK; return S_OK;
}; };
@ -246,9 +253,9 @@ public:
return S_OK; return S_OK;
} }
virtual HRESULT CreateMenuItemSeperator (IAvnMenuItem** ppv) override virtual HRESULT CreateMenuItemSeparator (IAvnMenuItem** ppv) override
{ {
*ppv = ::CreateAppMenuItemSeperator(); *ppv = ::CreateAppMenuItemSeparator();
return S_OK; return S_OK;
} }
@ -299,10 +306,24 @@ AvnPoint ToAvnPoint (NSPoint p)
AvnPoint ConvertPointY (AvnPoint p) AvnPoint ConvertPointY (AvnPoint p)
{ {
auto sw = [NSScreen.screens objectAtIndex:0].frame; auto primaryDisplayHeight = NSMaxY([[[NSScreen screens] firstObject] frame]);
auto t = MAX(sw.origin.y, sw.origin.y + sw.size.height); p.Y = primaryDisplayHeight - p.Y;
p.Y = t - p.Y;
return p; return p;
} }
CGFloat PrimaryDisplayHeight()
{
return NSMaxY([[[NSScreen screens] firstObject] frame]);
}
void SetAutoGenerateDefaultAppMenuItems (bool enabled)
{
s_generateDefaultAppMenuItems = enabled;
}
bool GetAutoGenerateDefaultAppMenuItems ()
{
return s_generateDefaultAppMenuItems;
}

11
native/Avalonia.Native/src/OSX/menu.h

@ -31,13 +31,13 @@ private:
NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem
IAvnActionCallback* _callback; IAvnActionCallback* _callback;
IAvnPredicateCallback* _predicate; IAvnPredicateCallback* _predicate;
bool _isSeperator; bool _isSeparator;
bool _isCheckable; bool _isCheckable;
public: public:
FORWARD_IUNKNOWN() FORWARD_IUNKNOWN()
AvnAppMenuItem(bool isSeperator); AvnAppMenuItem(bool isSeparator);
NSMenuItem* GetNative(); NSMenuItem* GetNative();
@ -45,7 +45,7 @@ public:
virtual HRESULT SetTitle (char* utf8String) override; virtual HRESULT SetTitle (char* utf8String) override;
virtual HRESULT SetGesture (char* key, AvnInputModifiers modifiers) override; virtual HRESULT SetGesture (AvnKey key, AvnInputModifiers modifiers) override;
virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override; virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override;
@ -60,7 +60,6 @@ public:
void RaiseOnClicked(); void RaiseOnClicked();
}; };
class AvnAppMenu : public ComSingleObject<IAvnMenu, &IID_IAvnMenu> class AvnAppMenu : public ComSingleObject<IAvnMenu, &IID_IAvnMenu>
{ {
private: private:
@ -71,10 +70,12 @@ public:
FORWARD_IUNKNOWN() FORWARD_IUNKNOWN()
AvnAppMenu(IAvnMenuEvents* events); AvnAppMenu(IAvnMenuEvents* events);
AvnMenu* GetNative(); AvnMenu* GetNative();
void RaiseNeedsUpdate (); void RaiseNeedsUpdate ();
void RaiseOpening();
void RaiseClosed();
virtual HRESULT InsertItem (int index, IAvnMenuItem* item) override; virtual HRESULT InsertItem (int index, IAvnMenuItem* item) override;

182
native/Avalonia.Native/src/OSX/menu.mm

@ -2,6 +2,9 @@
#include "common.h" #include "common.h"
#include "menu.h" #include "menu.h"
#include "window.h" #include "window.h"
#include "KeyTransform.h"
#include <CoreFoundation/CoreFoundation.h>
#include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */
@implementation AvnMenu @implementation AvnMenu
{ {
@ -68,12 +71,12 @@
} }
@end @end
AvnAppMenuItem::AvnAppMenuItem(bool isSeperator) AvnAppMenuItem::AvnAppMenuItem(bool isSeparator)
{ {
_isCheckable = false; _isCheckable = false;
_isSeperator = isSeperator; _isSeparator = isSeparator;
if(isSeperator) if(isSeparator)
{ {
_native = [NSMenuItem separatorItem]; _native = [NSMenuItem separatorItem];
} }
@ -122,23 +125,57 @@ HRESULT AvnAppMenuItem::SetTitle (char* utf8String)
} }
} }
HRESULT AvnAppMenuItem::SetGesture (char* key, AvnInputModifiers modifiers)
HRESULT AvnAppMenuItem::SetGesture (AvnKey key, AvnInputModifiers modifiers)
{ {
@autoreleasepool @autoreleasepool
{ {
NSEventModifierFlags flags = 0; if(key != AvnKeyNone)
{
if (modifiers & Control) NSEventModifierFlags flags = 0;
flags |= NSEventModifierFlagControl;
if (modifiers & Shift) if (modifiers & Control)
flags |= NSEventModifierFlagShift; flags |= NSEventModifierFlagControl;
if (modifiers & Alt) if (modifiers & Shift)
flags |= NSEventModifierFlagOption; flags |= NSEventModifierFlagShift;
if (modifiers & Windows) if (modifiers & Alt)
flags |= NSEventModifierFlagCommand; flags |= NSEventModifierFlagOption;
if (modifiers & Windows)
flags |= NSEventModifierFlagCommand;
auto it = s_UnicodeKeyMap.find(key);
if(it != s_UnicodeKeyMap.end())
{
auto keyString= [NSString stringWithFormat:@"%C", (unsigned short)it->second];
[_native setKeyEquivalent: keyString];
[_native setKeyEquivalentModifierMask:flags];
return S_OK;
}
else
{
auto it = s_AvnKeyMap.find(key); // check if a virtual key is mapped.
if(it != s_AvnKeyMap.end())
{
auto it1 = s_QwertyKeyMap.find(it->second); // convert virtual key to qwerty string.
if(it1 != s_QwertyKeyMap.end())
{
[_native setKeyEquivalent: [NSString stringWithUTF8String: it1->second]];
[_native setKeyEquivalentModifierMask:flags];
return S_OK;
}
}
}
}
[_native setKeyEquivalent:[NSString stringWithUTF8String:(const char*)key]]; // Nothing matched... clear.
[_native setKeyEquivalentModifierMask:flags]; [_native setKeyEquivalent: @""];
[_native setKeyEquivalentModifierMask: 0];
return S_OK; return S_OK;
} }
@ -261,6 +298,23 @@ void AvnAppMenu::RaiseNeedsUpdate()
} }
} }
void AvnAppMenu::RaiseOpening()
{
if(_baseEvents != nullptr)
{
_baseEvents->Opening();
}
}
void AvnAppMenu::RaiseClosed()
{
if(_baseEvents != nullptr)
{
_baseEvents->Closed();
}
}
HRESULT AvnAppMenu::InsertItem(int index, IAvnMenuItem *item) HRESULT AvnAppMenu::InsertItem(int index, IAvnMenuItem *item)
{ {
@autoreleasepool @autoreleasepool
@ -345,6 +399,15 @@ HRESULT AvnAppMenu::Clear()
_parent->RaiseNeedsUpdate(); _parent->RaiseNeedsUpdate();
} }
- (void)menuWillOpen:(NSMenu *)menu
{
_parent->RaiseOpening();
}
- (void)menuDidClose:(NSMenu *)menu
{
_parent->RaiseClosed();
}
@end @end
@ -364,7 +427,7 @@ extern IAvnMenuItem* CreateAppMenuItem()
} }
} }
extern IAvnMenuItem* CreateAppMenuItemSeperator() extern IAvnMenuItem* CreateAppMenuItemSeparator()
{ {
@autoreleasepool @autoreleasepool
{ {
@ -408,47 +471,50 @@ extern void SetAppMenu (NSString* appName, IAvnMenu* menu)
auto appMenu = [s_appMenuItem submenu]; auto appMenu = [s_appMenuItem submenu];
[appMenu addItem:[NSMenuItem separatorItem]]; if(GetAutoGenerateDefaultAppMenuItems())
{
// Services item and menu [appMenu addItem:[NSMenuItem separatorItem]];
auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.title = @"Services"; // Services item and menu
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"]; auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.submenu = servicesMenu; servicesItem.title = @"Services";
[NSApplication sharedApplication].servicesMenu = servicesMenu; NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
[appMenu addItem:servicesItem]; servicesItem.submenu = servicesMenu;
[NSApplication sharedApplication].servicesMenu = servicesMenu;
[appMenu addItem:[NSMenuItem separatorItem]]; [appMenu addItem:servicesItem];
// Hide Application [appMenu addItem:[NSMenuItem separatorItem]];
auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
// Hide Application
[appMenu addItem:hideItem]; auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
// Hide Others [appMenu addItem:hideItem];
auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:) // Hide Others
keyEquivalent:@"h"]; auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption; keyEquivalent:@"h"];
[appMenu addItem:hideAllOthersItem];
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption;
// Show All [appMenu addItem:hideAllOthersItem];
auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:) // Show All
keyEquivalent:@""]; auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
[appMenu addItem:showAllItem]; keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]]; [appMenu addItem:showAllItem];
// Quit Application [appMenu addItem:[NSMenuItem separatorItem]];
auto quitItem = [[NSMenuItem alloc] init];
quitItem.title = [@"Quit " stringByAppendingString:appName]; // Quit Application
quitItem.keyEquivalent = @"q"; auto quitItem = [[NSMenuItem alloc] init];
quitItem.target = [AvnWindow class]; quitItem.title = [@"Quit " stringByAppendingString:appName];
quitItem.action = @selector(closeAll); quitItem.keyEquivalent = @"q";
[appMenu addItem:quitItem]; quitItem.target = [AvnWindow class];
quitItem.action = @selector(closeAll);
[appMenu addItem:quitItem];
}
} }
else else
{ {

12
native/Avalonia.Native/src/OSX/rendertarget.mm

@ -111,7 +111,11 @@
if(_renderbuffer != 0) if(_renderbuffer != 0)
glDeleteRenderbuffers(1, &_renderbuffer); glDeleteRenderbuffers(1, &_renderbuffer);
} }
CFRelease(surface);
if(surface != nullptr)
{
CFRelease(surface);
}
} }
@end @end
@ -145,6 +149,12 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
} }
- (void)resize:(AvnPixelSize)size withScale: (float) scale{ - (void)resize:(AvnPixelSize)size withScale: (float) scale{
if(size.Height <= 0)
size.Height = 1;
if(size.Width <= 0)
size.Width = 1;
@synchronized (lock) { @synchronized (lock) {
if(surface == nil if(surface == nil
|| surface->size.Width != size.Width || surface->size.Width != size.Width

1
native/Avalonia.Native/src/OSX/window.h

@ -34,6 +34,7 @@ class WindowBaseImpl;
-(double) getScaling; -(double) getScaling;
-(double) getExtendedTitleBarHeight; -(double) getExtendedTitleBarHeight;
-(void) setIsExtended:(bool)value; -(void) setIsExtended:(bool)value;
-(void) updateShadow;
@end @end
struct INSWindowHolder struct INSWindowHolder

72
native/Avalonia.Native/src/OSX/window.mm

@ -106,13 +106,13 @@ public:
return Window; return Window;
} }
virtual HRESULT Show() override virtual HRESULT Show(bool activate) override
{ {
@autoreleasepool @autoreleasepool
{ {
SetPosition(lastPositionSet); SetPosition(lastPositionSet);
UpdateStyle(); UpdateStyle();
if(ShouldTakeFocusOnShow()) if(ShouldTakeFocusOnShow() && activate)
{ {
[Window makeKeyAndOrderFront:Window]; [Window makeKeyAndOrderFront:Window];
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
@ -124,7 +124,11 @@ public:
[Window setTitle:_lastTitle]; [Window setTitle:_lastTitle];
_shown = true; _shown = true;
dispatch_async(dispatch_get_main_queue(), ^{
[Window updateShadow];
});
return S_OK; return S_OK;
} }
} }
@ -561,11 +565,11 @@ private:
} }
} }
virtual HRESULT Show () override virtual HRESULT Show (bool activate) override
{ {
@autoreleasepool @autoreleasepool
{ {
WindowBaseImpl::Show(); WindowBaseImpl::Show(activate);
HideOrShowTrafficLights(); HideOrShowTrafficLights();
@ -1391,17 +1395,20 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[super viewDidChangeBackingProperties]; [super viewDidChangeBackingProperties];
} }
- (bool) ignoreUserInput - (bool) ignoreUserInput:(bool)trigerInputWhenDisabled
{ {
auto parentWindow = objc_cast<AvnWindow>([self window]); auto parentWindow = objc_cast<AvnWindow>([self window]);
if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
{ {
auto window = dynamic_cast<WindowImpl*>(_parent.getRaw()); if(trigerInputWhenDisabled)
if(window != nullptr)
{ {
window->WindowEvents->GotInputWhenDisabled(); auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
if(window != nullptr)
{
window->WindowEvents->GotInputWhenDisabled();
}
} }
return TRUE; return TRUE;
@ -1412,7 +1419,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
{ {
if([self ignoreUserInput]) bool triggerInputWhenDisabled = type != Move;
if([self ignoreUserInput: triggerInputWhenDisabled])
{ {
return; return;
} }
@ -1578,7 +1587,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type - (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
{ {
if([self ignoreUserInput]) if([self ignoreUserInput: false])
{ {
return; return;
} }
@ -1833,6 +1842,19 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
double _lastScaling; double _lastScaling;
} }
- (void)updateShadow
{
// Common problem in Cocoa where [invalidateShadow] does work,
// This hack forces Cocoa to invalidate the shadow.
NSRect frame = [self frame];
NSRect updatedFrame = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width + 1.0, frame.size.height + 1.0);
[self setFrame:updatedFrame display:YES];
[self setFrame:frame display:YES];
[self invalidateShadow];
}
-(void) setIsExtended:(bool)value; -(void) setIsExtended:(bool)value;
{ {
_isExtended = value; _isExtended = value;
@ -1872,7 +1894,12 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
for(int i = 0; i < numWindows; i++) for(int i = 0; i < numWindows; i++)
{ {
[[windows objectAtIndex:i] performClose:nil]; auto window = (AvnWindow*)[windows objectAtIndex:i];
if([window parentWindow] == nullptr) // Avalonia will handle the child windows.
{
[window performClose:nil];
}
} }
} }
@ -1926,6 +1953,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[NSApp setMenu:_menu]; [NSApp setMenu:_menu];
} }
else
{
[self showAppMenuOnly];
}
} }
-(void) showAppMenuOnly -(void) showAppMenuOnly
@ -2063,17 +2094,17 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
-(void)becomeKeyWindow -(void)becomeKeyWindow
{ {
[self showWindowMenuWithAppMenu];
if([self activateAppropriateChild: true]) if([self activateAppropriateChild: true])
{ {
[self showWindowMenuWithAppMenu];
if(_parent != nullptr) if(_parent != nullptr)
{ {
_parent->BaseEvents->Activated(); _parent->BaseEvents->Activated();
} }
[super becomeKeyWindow];
} }
[super becomeKeyWindow];
} }
-(void) restoreParentWindow; -(void) restoreParentWindow;
@ -2221,9 +2252,12 @@ protected:
{ {
@autoreleasepool @autoreleasepool
{ {
[Window setContentSize:NSSize{x, y}]; if (Window != nullptr)
{
[Window setContentSize:NSSize{x, y}];
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];
}
return S_OK; return S_OK;
} }

58
nukebuild/Build.cs

@ -16,7 +16,6 @@ using Nuke.Common.Tools.MSBuild;
using Nuke.Common.Tools.Npm; using Nuke.Common.Tools.Npm;
using Nuke.Common.Utilities; using Nuke.Common.Utilities;
using Nuke.Common.Utilities.Collections; using Nuke.Common.Utilities.Collections;
using Pharmacist.Core;
using static Nuke.Common.EnvironmentInfo; using static Nuke.Common.EnvironmentInfo;
using static Nuke.Common.IO.FileSystemTasks; using static Nuke.Common.IO.FileSystemTasks;
using static Nuke.Common.IO.PathConstruction; using static Nuke.Common.IO.PathConstruction;
@ -89,10 +88,6 @@ partial class Build : NukeBuild
Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit(); Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
} }
ExecWait("dotnet version:", "dotnet", "--version"); ExecWait("dotnet version:", "dotnet", "--version");
if (Parameters.IsRunningOnUnix)
ExecWait("Mono version:", "mono", "--version");
} }
IReadOnlyCollection<Output> MsBuildCommon( IReadOnlyCollection<Output> MsBuildCommon(
@ -167,44 +162,8 @@ partial class Build : NukeBuild
.AddProperty("PackageVersion", Parameters.Version) .AddProperty("PackageVersion", Parameters.Version)
.SetConfiguration(Parameters.Configuration) .SetConfiguration(Parameters.Configuration)
); );
await CompileReactiveEvents();
}); });
async Task CompileReactiveEvents()
{
var avaloniaBuildOutput = Path.Combine(RootDirectory, "packages", "Avalonia", "bin", Parameters.Configuration);
var avaloniaAssemblies = GlobFiles(avaloniaBuildOutput, "**/Avalonia*.dll")
.Where(file => !file.Contains("Avalonia.Build.Tasks") &&
!file.Contains("Avalonia.Remote.Protocol"));
var eventsDirectory = GlobDirectories($"{RootDirectory}/src/**/Avalonia.ReactiveUI.Events").First();
var eventsBuildFile = Path.Combine(eventsDirectory, "Events_Avalonia.cs");
if (File.Exists(eventsBuildFile))
File.Delete(eventsBuildFile);
using (var stream = File.Create(eventsBuildFile))
using (var writer = new StreamWriter(stream))
{
await ObservablesForEventGenerator.ExtractEventsFromAssemblies(
writer, avaloniaAssemblies, new string[0], "netstandard2.0"
);
}
var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj");
if (Parameters.IsRunningOnWindows)
MsBuildCommon(eventsProject, c => c
.SetProcessArgumentConfigurator(a => a.Add("/r"))
.AddTargets("Build")
);
else
DotNetBuild(c => c
.SetProjectFile(eventsProject)
.AddProperty("PackageVersion", Parameters.Version)
.SetConfiguration(Parameters.Configuration)
);
}
void RunCoreTest(string projectName) void RunCoreTest(string projectName)
{ {
Information($"Running tests from {projectName}"); Information($"Running tests from {projectName}");
@ -305,14 +264,19 @@ partial class Build : NukeBuild
.Executes(() => .Executes(() =>
{ {
var data = Parameters; var data = Parameters;
var pathToProjectSource = RootDirectory / "samples" / "ControlCatalog.NetCore";
var pathToPublish = pathToProjectSource / "bin" / data.Configuration / "publish";
DotNetPublish(c => c
.SetProject(pathToProjectSource / "ControlCatalog.NetCore.csproj")
.EnableNoBuild()
.SetConfiguration(data.Configuration)
.AddProperty("PackageVersion", data.Version)
.AddProperty("PublishDir", pathToPublish));
Zip(data.ZipCoreArtifacts, data.BinRoot); Zip(data.ZipCoreArtifacts, data.BinRoot);
Zip(data.ZipNuGetArtifacts, data.NugetRoot); Zip(data.ZipNuGetArtifacts, data.NugetRoot);
Zip(data.ZipTargetControlCatalogDesktopDir, Zip(data.ZipTargetControlCatalogNetCoreDir, pathToPublish);
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dll").Concat(
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.config")).Concat(
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.so")).Concat(
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dylib")).Concat(
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.exe")));
}); });
Target CreateIntermediateNugetPackages => _ => _ Target CreateIntermediateNugetPackages => _ => _

7
nukebuild/BuildParameters.cs

@ -58,8 +58,7 @@ public partial class Build
public string FileZipSuffix { get; } public string FileZipSuffix { get; }
public AbsolutePath ZipCoreArtifacts { get; } public AbsolutePath ZipCoreArtifacts { get; }
public AbsolutePath ZipNuGetArtifacts { get; } public AbsolutePath ZipNuGetArtifacts { get; }
public AbsolutePath ZipSourceControlCatalogDesktopDir { get; } public AbsolutePath ZipTargetControlCatalogNetCoreDir { get; }
public AbsolutePath ZipTargetControlCatalogDesktopDir { get; }
public BuildParameters(Build b) public BuildParameters(Build b)
@ -129,9 +128,7 @@ public partial class Build
FileZipSuffix = Version + ".zip"; FileZipSuffix = Version + ".zip";
ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix); ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix);
ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix); ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix);
ZipSourceControlCatalogDesktopDir = ZipTargetControlCatalogNetCoreDir = ZipRoot / ("ControlCatalog.NetCore-" + FileZipSuffix);
RootDirectory / ("samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461");
ZipTargetControlCatalogDesktopDir = ZipRoot / ("ControlCatalog.Desktop-" + FileZipSuffix);
} }
string GetVersion() string GetVersion()

1
nukebuild/_build.csproj

@ -17,7 +17,6 @@
<PackageReference Include="ILRepack.NETStandard" Version="2.0.4" /> <PackageReference Include="ILRepack.NETStandard" Version="2.0.4" />
<!-- Keep in sync with Avalonia.Build.Tasks --> <!-- Keep in sync with Avalonia.Build.Tasks -->
<PackageReference Include="Mono.Cecil" Version="0.11.2" /> <PackageReference Include="Mono.Cecil" Version="0.11.2" />
<PackageReference Include="Pharmacist.Core" Version="1.8.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

12
packages/Avalonia/Avalonia.csproj

@ -6,7 +6,9 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" /> <ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" />
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" /> <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" >
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
@ -29,21 +31,23 @@
</_PackageFiles> </_PackageFiles>
</ItemGroup> </ItemGroup>
</Target> </Target>
<ItemGroup> <ItemGroup>
<Content Include="*.props"> <Content Include="*.props">
<Pack>true</Pack> <Pack>true</Pack>
<PackagePath>build\</PackagePath> <PackagePath>build\;buildTransitive\</PackagePath>
</Content> </Content>
<Content Include="*.targets"> <Content Include="*.targets">
<Pack>true</Pack> <Pack>true</Pack>
<PackagePath>build\</PackagePath> <PackagePath>build\;buildTransitive\</PackagePath>
</Content> </Content>
<Content Include="AvaloniaItemSchema.xaml"> <Content Include="AvaloniaItemSchema.xaml">
<Pack>true</Pack> <Pack>true</Pack>
<PackagePath>build\</PackagePath> <PackagePath>build\;buildTransitive\</PackagePath>
</Content> </Content>
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\SharedVersion.props" /> <Import Project="..\..\build\SharedVersion.props" />
<Import Project="..\..\build\NetFX.props" /> <Import Project="..\..\build\NetFX.props" />
<Import Project="..\..\build\CoreLibraries.props" /> <Import Project="..\..\build\CoreLibraries.props" />
<Import Project="..\..\build\SourceLink.props" Condition="'$(DisableSourceLink)' == ''" />
</Project> </Project>

13
readme.md

@ -2,15 +2,13 @@
<br /> <br />
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) [![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg)
<img alt="Avalonia" src="https://user-images.githubusercontent.com/6759207/84897744-cab6d800-b0ae-11ea-8214-e5174d71f5c8.png" width="400"/>
## 📖 About AvaloniaUI ## 📖 About AvaloniaUI
Avalonia is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows via .NET Framework and .NET Core, Linux via Xorg, macOS. Avalonia is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. Avalonia is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows via .NET Framework and .NET Core, Linux via Xorg, macOS. Avalonia is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development.
<img src="https://user-images.githubusercontent.com/6759207/84751662-7c79da00-afc5-11ea-8780-dda28db70b76.png" width="700" /> <img src="https://user-images.githubusercontent.com/6759207/84751662-7c79da00-afc5-11ea-8780-dda28db70b76.png" width="700" />
> **Note:** The UI theme you see in the picture above is still work-in-progress and will be available in the upcoming Avalonia 0.10.0 release. However, you can connect to our nightly build feed and install latest pre-release versions of Avalonia NuGet packages, if you are willing to help out with the development and testing. See [Using nightly build feed](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) for more info. ([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia! To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
@ -31,15 +29,22 @@ Install-Package Avalonia.Desktop
Examples of UIs built with Avalonia Examples of UIs built with Avalonia
![image](https://user-images.githubusercontent.com/4672627/84707589-5b69a880-af35-11ea-87a6-7ad57a31d314.png) ![image](https://user-images.githubusercontent.com/4672627/84707589-5b69a880-af35-11ea-87a6-7ad57a31d314.png)
([Synfonia](https://github.com/jmacato/Synfonia))
![image](https://user-images.githubusercontent.com/4672627/85069644-d8419000-b18a-11ea-8732-be9055bb61fd.PNG) ![image](https://user-images.githubusercontent.com/4672627/85069644-d8419000-b18a-11ea-8732-be9055bb61fd.PNG)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
![image](https://user-images.githubusercontent.com/4672627/85069659-dc6dad80-b18a-11ea-8375-39ef95315b5c.PNG) ![image](https://user-images.githubusercontent.com/4672627/85069659-dc6dad80-b18a-11ea-8375-39ef95315b5c.PNG)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
![image](https://user-images.githubusercontent.com/4672627/84708947-c3b98980-af37-11ea-8c9d-503334615bbf.png) ![image](https://user-images.githubusercontent.com/4672627/84708947-c3b98980-af37-11ea-8c9d-503334615bbf.png)
([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery))
## JetBrains Rider ## JetBrains Rider
If you need to develop Avalonia app with JetBrains Rider you can use latest Rider [preview builds](https://www.jetbrains.com/rider/nextversion/). [JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
## Bleeding Edge Builds ## Bleeding Edge Builds

14
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -16,7 +16,7 @@
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile> <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk> <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion> <TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -71,21 +71,23 @@
<Compile Include="MainActivity.cs" /> <Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" /> <Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SplashActivity.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Resources\AboutResources.txt" /> <None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" /> <None Include="Assets\AboutAssets.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\layout\Main.axml"> <AndroidResource Include="Resources\drawable\splash_screen.xml" />
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\values\Strings.xml" /> <AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\values\styles.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\drawable\Icon.png" /> <AndroidResource Include="..\..\build\Assets\Icon.png">
<Link>Resources\drawable\Icon.png</Link>
</AndroidResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Properties\AndroidManifest.xml" /> <None Include="Properties\AndroidManifest.xml" />

21
samples/ControlCatalog.Android/MainActivity.cs

@ -1,31 +1,18 @@
using System; using Android.App;
using Android.App;
using Android.OS; using Android.OS;
using Android.Content.PM; using Android.Content.PM;
using Avalonia.Android; using Avalonia.Android;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Themes.Default;
using Avalonia;
namespace ControlCatalog.Android namespace ControlCatalog.Android
{ {
[Activity(Label = "ControlCatalog.Android", MainLauncher = true, Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)] [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)]
public class MainActivity : AvaloniaActivity public class MainActivity : AvaloniaActivity
{ {
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
if (Avalonia.Application.Current == null)
{
AppBuilder.Configure<App>()
.UseAndroid()
.SetupWithoutStarting();
Content = new MainView();
}
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
Content = new MainView();
} }
} }
} }

2
samples/ControlCatalog.Android/Properties/AndroidManifest.xml

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ControlCatalog.Android" android:versionCode="1" android:versionName="1.0" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ControlCatalog.Android" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk /> <uses-sdk android:targetSdkVersion="30" />
<application android:label="ControlCatalog.Android"></application> <application android:label="ControlCatalog.Android"></application>
</manifest> </manifest>

59
samples/ControlCatalog.Android/Resources/Resource.Designer.cs

@ -2,7 +2,6 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // This code was generated by a tool.
// Runtime Version:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@ -15,7 +14,7 @@ namespace ControlCatalog.Android
{ {
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
public partial class Resource public partial class Resource
{ {
@ -26,8 +25,6 @@ namespace ControlCatalog.Android
public static void UpdateIdValues() public static void UpdateIdValues()
{ {
global::Avalonia.Android.Resource.String.ApplicationName = global::ControlCatalog.Android.Resource.String.ApplicationName;
global::Avalonia.Android.Resource.String.Hello = global::ControlCatalog.Android.Resource.String.Hello;
} }
public partial class Attribute public partial class Attribute
@ -43,69 +40,59 @@ namespace ControlCatalog.Android
} }
} }
public partial class Drawable public partial class Color
{ {
// aapt resource value: 0x7f020000 // aapt resource value: 0x7F010000
public const int Icon = 2130837504; public const int splash_background = 2130771968;
static Drawable() static Color()
{ {
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); global::Android.Runtime.ResourceIdManager.UpdateIdValues();
} }
private Drawable() private Color()
{ {
} }
} }
public partial class Id public partial class Drawable
{ {
// aapt resource value: 0x7f050000 // aapt resource value: 0x7F020000
public const int MyButton = 2131034112; public const int Icon = 2130837504;
static Id()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Id()
{
}
}
public partial class Layout
{
// aapt resource value: 0x7f030000 // aapt resource value: 0x7F020001
public const int Main = 2130903040; public const int splash_screen = 2130837505;
static Layout() static Drawable()
{ {
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); global::Android.Runtime.ResourceIdManager.UpdateIdValues();
} }
private Layout() private Drawable()
{ {
} }
} }
public partial class String public partial class Style
{ {
// aapt resource value: 0x7f040001 // aapt resource value: 0x7F030000
public const int ApplicationName = 2130968577; public const int MyTheme = 2130903040;
// aapt resource value: 0x7F030001
public const int MyTheme_NoActionBar = 2130903041;
// aapt resource value: 0x7f040000 // aapt resource value: 0x7F030002
public const int Hello = 2130968576; public const int MyTheme_Splash = 2130903042;
static String() static Style()
{ {
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); global::Android.Runtime.ResourceIdManager.UpdateIdValues();
} }
private String() private Style()
{ {
} }
} }

BIN
samples/ControlCatalog.Android/Resources/drawable/Icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

13
samples/ControlCatalog.Android/Resources/drawable/splash_screen.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="@color/splash_background"/>
</item>
<item android:drawable="@drawable/icon"
android:width="120dp"
android:height="120dp"
android:gravity="center" />
</layer-list>

13
samples/ControlCatalog.Android/Resources/layout/Main.axml

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/Hello"
/>
</LinearLayout>

5
samples/ControlCatalog.Android/Resources/values/Strings.xml

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="Hello">Hello World, Click Me!</string>
<string name="ApplicationName">ControlCatalog.Android</string>
</resources>

4
samples/ControlCatalog.Android/Resources/values/colors.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="splash_background">#FFFFFF</color>
</resources>

17
samples/ControlCatalog.Android/Resources/values/styles.xml

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="MyTheme">
</style>
<style name="MyTheme.NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="MyTheme.Splash" parent ="MyTheme.NoActionBar">
<item name="android:windowBackground">@drawable/splash_screen</item>
<item name="android:windowContentOverlay">@null</item>
</style>
</resources>

32
samples/ControlCatalog.Android/SplashActivity.cs

@ -0,0 +1,32 @@
using Android.App;
using Android.Content;
using Android.OS;
using Application = Android.App.Application;
using Avalonia;
namespace ControlCatalog.Android
{
[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
protected override void OnResume()
{
base.OnResume();
if (Avalonia.Application.Current == null)
{
AppBuilder.Configure<App>()
.UseAndroid()
.SetupWithoutStarting();
}
StartActivity(new Intent(Application.Context, typeof(MainActivity)));
}
}
}

6
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup> </PropertyGroup>
@ -15,6 +15,10 @@
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" /> <PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<!-- For Microsoft.CodeAnalysis -->
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
</PropertyGroup>
<Import Project="..\..\build\SampleApp.props" /> <Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" /> <Import Project="..\..\build\ReferenceCoreLibraries.props" />

6
samples/ControlCatalog.NetCore/Program.cs

@ -109,12 +109,12 @@ namespace ControlCatalog.NetCore
.With(new X11PlatformOptions .With(new X11PlatformOptions
{ {
EnableMultiTouch = true, EnableMultiTouch = true,
UseDBusMenu = true UseDBusMenu = true,
EnableIme = true,
}) })
.With(new Win32PlatformOptions .With(new Win32PlatformOptions
{ {
EnableMultitouch = true, EnableMultitouch = true
AllowEglInitialization = true
}) })
.UseSkia() .UseSkia()
.UseManagedSystemDialogs() .UseManagedSystemDialogs()

8
samples/ControlCatalog/App.xaml.cs

@ -39,6 +39,10 @@ namespace ControlCatalog
public static Styles DefaultLight = new Styles public static Styles DefaultLight = new Styles
{ {
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{ {
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
@ -60,6 +64,10 @@ namespace ControlCatalog
public static Styles DefaultDark = new Styles public static Styles DefaultDark = new Styles
{ {
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{ {
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")

BIN
samples/ControlCatalog/Assets/Fonts/WenQuanYiMicroHei-01.ttf

Binary file not shown.

BIN
samples/ControlCatalog/Assets/avalonia-32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

2
samples/ControlCatalog/ControlCatalog.csproj

@ -27,6 +27,6 @@
<ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
<ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" /> <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" /> <Import Project="..\..\build\BuildTargets.targets" />
</Project> </Project>

10
samples/ControlCatalog/MainView.xaml

@ -21,7 +21,14 @@
<TabItem Header="Carousel"><pages:CarouselPage/></TabItem> <TabItem Header="Carousel"><pages:CarouselPage/></TabItem>
<TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem> <TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem>
<TabItem Header="ComboBox"><pages:ComboBoxPage/></TabItem> <TabItem Header="ComboBox"><pages:ComboBoxPage/></TabItem>
<TabItem Header="ContextFlyout">
<pages:ContextFlyoutPage/>
</TabItem>
<TabItem Header="ContextMenu"><pages:ContextMenuPage/></TabItem> <TabItem Header="ContextMenu"><pages:ContextMenuPage/></TabItem>
<TabItem Header="Cursor"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:CursorPage/>
</TabItem>
<TabItem Header="DataGrid" <TabItem Header="DataGrid"
ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"> ScrollViewer.HorizontalScrollBarVisibility="Disabled">
@ -34,6 +41,9 @@
<pages:CalendarDatePickerPage/></TabItem> <pages:CalendarDatePickerPage/></TabItem>
<TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem> <TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem>
<TabItem Header="Expander"><pages:ExpanderPage/></TabItem> <TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
<TabItem Header="Flyouts">
<pages:FlyoutsPage />
</TabItem>
<TabItem Header="Image" <TabItem Header="Image"
ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"> ScrollViewer.HorizontalScrollBarVisibility="Disabled">

4
samples/ControlCatalog/MainWindow.xaml

@ -18,11 +18,11 @@
<NativeMenuItem Header="File"> <NativeMenuItem Header="File">
<NativeMenu> <NativeMenu>
<NativeMenuItem Icon="/Assets/test_icon.ico" Header="Open" Clicked="OnOpenClicked" Gesture="Ctrl+O"/> <NativeMenuItem Icon="/Assets/test_icon.ico" Header="Open" Clicked="OnOpenClicked" Gesture="Ctrl+O"/>
<NativeMenuItemSeperator/> <NativeMenuItemSeperator/><!-- Uses incorrect spelling to demonstrate backwards compatibility -->
<NativeMenuItem Icon="/Assets/github_icon.png" Header="Recent"> <NativeMenuItem Icon="/Assets/github_icon.png" Header="Recent">
<NativeMenu/> <NativeMenu/>
</NativeMenuItem> </NativeMenuItem>
<NativeMenuItemSeperator/> <NativeMenuItemSeparator/>
<NativeMenuItem Header="{x:Static local:MainWindow.MenuQuitHeader}" <NativeMenuItem Header="{x:Static local:MainWindow.MenuQuitHeader}"
Gesture="{x:Static local:MainWindow.MenuQuitGesture}" Gesture="{x:Static local:MainWindow.MenuQuitGesture}"
Clicked="OnCloseClicked" /> Clicked="OnCloseClicked" />

8
samples/ControlCatalog/Pages/AcrylicPage.xaml

@ -16,13 +16,13 @@
<StackPanel Spacing="5" Margin="40 10"> <StackPanel Spacing="5" Margin="40 10">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="TintOpacity" Foreground="Black" /> <TextBlock Text="TintOpacity" Foreground="Black" />
<Slider Name="TintOpacitySlider" Minimum="0" Maximum="1" Value="0.9" Width="400" /> <Slider Name="TintOpacitySlider" Minimum="0" Maximum="1" Value="0.9" SmallChange="0.1" LargeChange="0.2" Width="400" />
<TextBlock Text="{Binding #TintOpacitySlider.Value}" Foreground="Black" /> <TextBlock Text="{Binding #TintOpacitySlider.Value, StringFormat=\{0:0.#\}}" Foreground="Black" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="MaterialOpacity" Foreground="Black" /> <TextBlock Text="MaterialOpacity" Foreground="Black" />
<Slider Name="MaterialOpacitySlider" Minimum="0" Maximum="1" Value="0.8" Width="400" /> <Slider Name="MaterialOpacitySlider" Minimum="0" Maximum="1" Value="0.8" SmallChange="0.1" LargeChange="0.2" Width="400" />
<TextBlock Text="{Binding #MaterialOpacitySlider.Value}" Foreground="Black" /> <TextBlock Text="{Binding #MaterialOpacitySlider.Value, StringFormat=\{0:0.#\}}" Foreground="Black" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</ExperimentalAcrylicBorder> </ExperimentalAcrylicBorder>

102
samples/ControlCatalog/Pages/ContextFlyoutPage.axaml

@ -0,0 +1,102 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.ContextFlyoutPage">
<UserControl.Styles>
<Style Selector="FlyoutPresenter.NoPadding">
<Setter Property="Padding" Value="0" />
</Style>
</UserControl.Styles>
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Context Flyout</TextBlock>
<TextBlock Classes="h2">A right click Flyout that can be applied to any control.</TextBlock>
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
<Border Background="{DynamicResource SystemAccentColor}"
Margin="16"
Padding="48,48,48,48">
<Border.ContextFlyout>
<MenuFlyout>
<MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A" />
<MenuItem Header="_Disabled Menu Item" IsEnabled="False" InputGesture="Ctrl+D" />
<Separator/>
<MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1"/>
<MenuItem Header="Submenu _2"/>
</MenuItem>
<MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
<MenuItem.Icon>
<Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item with _Checkbox">
<MenuItem.Icon>
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
</MenuItem.Icon>
</MenuItem>
</MenuFlyout>
</Border.ContextFlyout>
<TextBlock Text="Defined in XAML"/>
</Border>
<Border Background="{DynamicResource SystemAccentColor}"
Margin="16"
Padding="48,48,48,48">
<Border.ContextMenu>
<ContextMenu Items="{Binding MenuItems}">
<ContextMenu.Styles>
<Style Selector="MenuItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Items" Value="{Binding Items}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style>
</ContextMenu.Styles>
</ContextMenu>
</Border.ContextMenu>
<TextBlock Text="Dynamically Generated"/>
</Border>
</StackPanel>
<TextBlock Text="Custom ContextFlyout for TextBox" />
<TextBox Name="TextBox" Width="150" HorizontalAlignment="Center" ContextMenu="{x:Null}">
<TextBox.ContextFlyout>
<Flyout FlyoutPresenterClasses="NoPadding">
<StackPanel Orientation="Horizontal">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Height" Value="40" />
<Setter Property="Width" Value="40" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<Style Selector="Button:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Opacity" Value="0.5" />
</Style>
</StackPanel.Styles>
<Button Name="CutButton" Command="{Binding $parent[TextBox].Cut}" IsEnabled="{Binding $parent[TextBox].CanCut}">
<PathIcon Width="14" Height="14" Data="M5.22774,2.08072 C5.43359778,1.94704 5.7011484,1.98419259 5.86368634,2.15675215 L5.91939,2.22774 L12.5191,12.3904 C12.956,12.1419 13.4614,12.0000019 14,12.0000019 C15.6569,12.0000019 17,13.3431 17,15.0000019 C17,16.6569 15.6569,18.0000019 14,18.0000019 C12.3431,18.0000019 11,16.6569 11,15.0000019 C11,14.3201402 11.226152,13.693011 11.6073785,13.1899092 L11.7401,13.0269 L10,10.3474 L8.25991,13.0269 C8.72078,13.5543 9,14.2446 9,15.0000019 C9,16.6569 7.65685,18.0000019 6,18.0000019 C4.34315,18.0000019 3,16.6569 3,15.0000019 C3,13.3431 4.34315,12.0000019 6,12.0000019 C6.46163143,12.0000019 6.89890041,12.1042536 7.28955831,12.2905296 L7.4809,12.3904 L9.40382,9.42936 L5.08072,2.77238 C4.93033,2.54079 4.99615,2.23112 5.22774,2.08072 Z M14,13 C12.8954,13 12,13.8954 12,15 C12,16.1046 12.8954,17 14,17 C15.1046,17 16,16.1046 16,15 C16,13.8954 15.1046,13 14,13 Z M6,13 C4.89543,13 4,13.8954 4,15 C4,16.1046 4.89543,17 6,17 C7.10457,17 8,16.1046 8,15 C8,13.8954 7.10457,13 6,13 Z M14.7723,2.08072 C15.0039,2.23112 15.0697,2.54079 14.9193,2.77238 L11.1924,8.51133 L10.5962,7.59329 L14.0806,2.22774 C14.231,1.99615 14.5407,1.93033 14.7723,2.08072 Z" />
</Button>
<Button Name="CopyButton" Content="Copy" Command="{Binding $parent[TextBox].Copy}" IsEnabled="{Binding $parent[TextBox].CanCopy}">
<PathIcon Width="14" Height="14" Data="M5.50280381,4.62704038 L5.5,6.75 L5.5,17.2542087 C5.5,19.0491342 6.95507456,20.5042087 8.75,20.5042087 L17.3662868,20.5044622 C17.057338,21.3782241 16.2239751,22.0042087 15.2444057,22.0042087 L8.75,22.0042087 C6.12664744,22.0042087 4,19.8775613 4,17.2542087 L4,6.75 C4,5.76928848 4.62744523,4.93512464 5.50280381,4.62704038 Z M17.75,2 C18.9926407,2 20,3.00735931 20,4.25 L20,17.25 C20,18.4926407 18.9926407,19.5 17.75,19.5 L8.75,19.5 C7.50735931,19.5 6.5,18.4926407 6.5,17.25 L6.5,4.25 C6.5,3.00735931 7.50735931,2 8.75,2 L17.75,2 Z M17.75,3.5 L8.75,3.5 C8.33578644,3.5 8,3.83578644 8,4.25 L8,17.25 C8,17.6642136 8.33578644,18 8.75,18 L17.75,18 C18.1642136,18 18.5,17.6642136 18.5,17.25 L18.5,4.25 C18.5,3.83578644 18.1642136,3.5 17.75,3.5 Z" />
</Button>
<Button Name="PasteButton" Content="Paste" Command="{Binding $parent[TextBox].Paste}" IsEnabled="{Binding $parent[TextBox].CanPaste}">
<PathIcon Width="14" Height="14" Data="M13.75,2 C14.940864,2 15.9156449,2.92516159 15.9948092,4.09595119 L16,4.25 L16,4.25 C16,4.16530567 15.9953205,4.0817043 15.9862059,3.99944035 L17.75,4 C18.9926407,4 20,5.00735931 20,6.25 L20,19.75 C20,20.9926407 18.9926407,22 17.75,22 L6.25,22 C5.00735931,22 4,20.9926407 4,19.75 L4,6.25 C4,5.00735931 5.00735931,4 6.25,4 L8.01379413,3.99944035 C8.00733496,4.05773764 8.00310309,4.11670658 8.00118552,4.17626017 L8,4.25 C8,3.00735931 9.00735931,2 10.25,2 L13.75,2 Z M13.75,6.5 L10.25,6.5 C9.45594921,6.5 8.75796956,6.08867052 8.357512,5.4674625 L8.37902077,5.50019943 L8.37902077,5.50019943 L6.25,5.5 C5.83578644,5.5 5.5,5.83578644 5.5,6.25 L5.5,19.75 C5.5,20.1642136 5.83578644,20.5 6.25,20.5 L17.75,20.5 C18.1642136,20.5 18.5,20.1642136 18.5,19.75 L18.5,6.25 C18.5,5.83578644 18.1642136,5.5 17.75,5.5 L15.6209792,5.50019943 L15.642488,5.4674625 C15.2420304,6.08867052 14.5440508,6.5 13.75,6.5 Z M13.75,3.5 L10.25,3.5 C9.83578644,3.5 9.5,3.83578644 9.5,4.25 C9.5,4.66421356 9.83578644,5 10.25,5 L13.75,5 C14.1642136,5 14.5,4.66421356 14.5,4.25 C14.5,3.83578644 14.1642136,3.5 13.75,3.5 Z" />
</Button>
<Button Name="ClearButton" Content="Clear" Command="{Binding $parent[TextBox].Clear}">
<PathIcon Width="14" Height="14" Data="M3.52499419,3.71761187 L3.61611652,3.61611652 C4.0717282,3.16050485 4.79154862,3.13013074 5.28238813,3.52499419 L5.38388348,3.61611652 L14,12.233 L22.6161165,3.61611652 C23.1042719,3.12796116 23.8957281,3.12796116 24.3838835,3.61611652 C24.8720388,4.10427189 24.8720388,4.89572811 24.3838835,5.38388348 L15.767,14 L24.3838835,22.6161165 C24.8394952,23.0717282 24.8698693,23.7915486 24.4750058,24.2823881 L24.3838835,24.3838835 C23.9282718,24.8394952 23.2084514,24.8698693 22.7176119,24.4750058 L22.6161165,24.3838835 L14,15.767 L5.38388348,24.3838835 C4.89572811,24.8720388 4.10427189,24.8720388 3.61611652,24.3838835 C3.12796116,23.8957281 3.12796116,23.1042719 3.61611652,22.6161165 L12.233,14 L3.61611652,5.38388348 C3.16050485,4.9282718 3.13013074,4.20845138 3.52499419,3.71761187 L3.61611652,3.61611652 L3.52499419,3.71761187 Z" />
</Button>
</StackPanel>
</Flyout>
</TextBox.ContextFlyout>
</TextBox>
</StackPanel>
</UserControl>

45
samples/ControlCatalog/Pages/ContextFlyoutPage.axaml.cs

@ -0,0 +1,45 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public class ContextFlyoutPage : UserControl
{
private TextBox _textBox;
public ContextFlyoutPage()
{
InitializeComponent();
var vm = new ContextFlyoutPageViewModel();
vm.View = this;
DataContext = vm;
_textBox = this.FindControl<TextBox>("TextBox");
var cutButton = this.FindControl<Button>("CutButton");
cutButton.Click += CloseFlyout;
var copyButton = this.FindControl<Button>("CopyButton");
copyButton.Click += CloseFlyout;
var pasteButton = this.FindControl<Button>("PasteButton");
pasteButton.Click += CloseFlyout;
var clearButton = this.FindControl<Button>("ClearButton");
clearButton.Click += CloseFlyout;
}
private void CloseFlyout(object sender, RoutedEventArgs e)
{
_textBox.ContextFlyout.Hide();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

1
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -31,6 +31,7 @@
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/> <CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item that won't close on click" StaysOpenOnClick="True" />
</ContextMenu> </ContextMenu>
</Border.ContextMenu> </Border.ContextMenu>
<TextBlock Text="Defined in XAML"/> <TextBlock Text="Defined in XAML"/>

29
samples/ControlCatalog/Pages/CursorPage.xaml

@ -0,0 +1,29 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CursorPage">
<Grid ColumnDefinitions="*,*" RowDefinitions="Auto,*">
<StackPanel Grid.ColumnSpan="2" Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Cursor</TextBlock>
<TextBlock Classes="h2">Defines a cursor (mouse pointer)</TextBlock>
</StackPanel>
<ListBox Grid.Row="1" Items="{Binding StandardCursors}" Margin="0 8 8 8">
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Cursor" Value="{Binding Cursor}"/>
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Type}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Column="1" Grid.Row="1" Margin="8 8 0 8">
<Button Cursor="{Binding CustomCursor}" Margin="0 8" Padding="16">
<TextBlock>Custom Cursor</TextBlock>
</Button>
</StackPanel>
</Grid>
</UserControl>

20
samples/ControlCatalog/Pages/CursorPage.xaml.cs

@ -0,0 +1,20 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class CursorPage : UserControl
{
public CursorPage()
{
this.InitializeComponent();
DataContext = new CursorPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

9
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -1,5 +1,5 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:local="clr-namespace:ControlCatalog.Models;assembly=ControlCatalog" xmlns:local="using:ControlCatalog.Models"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DataGridPage"> x:Class="ControlCatalog.Pages.DataGridPage">
<UserControl.Resources> <UserControl.Resources>
@ -26,7 +26,8 @@
<DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"> <DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="Country" Binding="{Binding Name}" Width="6*" /> <DataGridTextColumn Header="Country" Binding="{Binding Name}" Width="6*" />
<DataGridTextColumn Header="Region" Binding="{Binding Region}" Width="4*" /> <!-- CompiledBinding example of usage. -->
<DataGridTextColumn Header="Region" Binding="{CompiledBinding Region}" Width="4*" x:DataType="local:Country" />
<DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" /> <DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" />
<DataGridTextColumn Header="Area" Binding="{Binding Area}" Width="3*" /> <DataGridTextColumn Header="Area" Binding="{Binding Area}" Width="3*" />
<DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*" CellStyleClasses="gdp" /> <DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*" CellStyleClasses="gdp" />
@ -38,8 +39,8 @@
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="Country" Binding="{Binding Name}" Width="6*" /> <DataGridTextColumn Header="Country" Binding="{Binding Name}" Width="6*" />
<DataGridTextColumn Header="Region" Binding="{Binding Region}" Width="4*" /> <DataGridTextColumn Header="Region" Binding="{Binding Region}" Width="4*" />
<DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" /> <DataGridTextColumn DisplayIndex="3" Header="Population" Binding="{Binding Population}" Width="3*" />
<DataGridTextColumn Header="Area" Binding="{Binding Area}" Width="3*" /> <DataGridTextColumn DisplayIndex="2" Header="Area" Binding="{Binding Area}" Width="3*" />
<DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*" /> <DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*" />
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>

6
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@ -24,8 +24,10 @@ namespace ControlCatalog.Pages
dg1.LoadingRow += Dg1_LoadingRow; dg1.LoadingRow += Dg1_LoadingRow;
dg1.Sorting += (s, a) => dg1.Sorting += (s, a) =>
{ {
var property = ((a.Column as DataGridBoundColumn)?.Binding as Binding).Path; var binding = (a.Column as DataGridBoundColumn)?.Binding as Binding;
if (property == dataGridSortDescription.PropertyPath
if (binding?.Path is string property
&& property == dataGridSortDescription.PropertyPath
&& !collectionView1.SortDescriptions.Contains(dataGridSortDescription)) && !collectionView1.SortDescriptions.Contains(dataGridSortDescription))
{ {
collectionView1.SortDescriptions.Add(dataGridSortDescription); collectionView1.SortDescriptions.Add(dataGridSortDescription);

264
samples/ControlCatalog/Pages/FlyoutsPage.axaml

@ -0,0 +1,264 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="700"
x:Class="ControlCatalog.Pages.FlyoutsPage">
<UserControl.Resources>
<MenuFlyout x:Key="SharedMenuFlyout">
<MenuItem Header="Item 1">
<MenuItem Header="Subitem 1" />
<MenuItem Header="Subitem 2" />
<MenuItem Header="Subitem 3" />
</MenuItem>
<MenuItem Header="Item 2" InputGesture="Ctrl+A" />
<MenuItem Header="Item 3" />
</MenuFlyout>
<Flyout Placement="Bottom" x:Key="BasicFlyout">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</UserControl.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Disabled">
<StackPanel Spacing="10">
<TextBlock FontSize="18" Text="Button with a Flyout" />
<StackPanel>
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<Button Content="Click Me!" Flyout="{StaticResource BasicFlyout}" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Name="ButtonFlyoutXamlText" Padding="15" />
</Panel>
</StackPanel>
<TextBlock FontSize="18" Text="MenuFlyout" />
<StackPanel>
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<Button Content="Click Me!" Flyout="{StaticResource SharedMenuFlyout}" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Name="MenuFlyoutXamlText" Padding="15" />
</Panel>
</StackPanel>
<TextBlock FontSize="18" Text="Attached Flyouts" />
<StackPanel>
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}"
HorizontalAlignment="Left"
Height="100"
Name="AttachedFlyoutPanel">
<FlyoutBase.AttachedFlyout>
<Flyout>
<Panel Height="100">
<TextBlock Text="Attached Flyout!"
VerticalAlignment="Center"
Margin="10"/>
</Panel>
</Flyout>
</FlyoutBase.AttachedFlyout>
<TextBlock Text="Double click panel to launch AttachedFlyout"
VerticalAlignment="Center"
Margin="10"/>
</Panel>
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Name="AttachedFlyoutXamlText" Padding="15" />
</Panel>
</StackPanel>
<TextBlock FontSize="18" Text="Sharing Flyouts" />
<StackPanel>
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<StackPanel Orientation="Horizontal" Spacing="30">
<Button Content="Launch Flyout on this button" Flyout="{StaticResource SharedMenuFlyout}"/>
<Button Content="Launch Flyout on this button" Flyout="{StaticResource SharedMenuFlyout}"/>
</StackPanel>
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Name="SharedFlyoutXamlText" Padding="15" />
</Panel>
</StackPanel>
<TextBlock FontSize="18" Text="Flyout Placements" />
<StackPanel>
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<UniformGrid Columns="3">
<UniformGrid.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="10" />
</Style>
</UniformGrid.Styles>
<Button Content="Placement=Top">
<Button.Flyout>
<Flyout Placement="Top">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=Bottom">
<Button.Flyout>
<Flyout Placement="Bottom">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=Left">
<Button.Flyout>
<Flyout Placement="Left">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=Right">
<Button.Flyout>
<Flyout Placement="Right">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=TopEdgeAlignedLeft">
<Button.Flyout>
<Flyout Placement="TopEdgeAlignedLeft">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=TopEdgeAlignedRight">
<Button.Flyout>
<Flyout Placement="TopEdgeAlignedRight">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=BottomEdgeAlignedLeft">
<Button.Flyout>
<Flyout Placement="BottomEdgeAlignedLeft">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=BottomEdgeAlignedRight">
<Button.Flyout>
<Flyout Placement="BottomEdgeAlignedRight">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=LeftEdgeAlignedTop">
<Button.Flyout>
<Flyout Placement="LeftEdgeAlignedTop">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=LeftEdgeAlignedBottom">
<Button.Flyout>
<Flyout Placement="LeftEdgeAlignedBottom">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=RightEdgeAlignedBottom">
<Button.Flyout>
<Flyout Placement="RightEdgeAlignedTop">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=RightEdgeAlignedBottom">
<Button.Flyout>
<Flyout Placement="RightEdgeAlignedBottom">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
</UniformGrid>
</Border>
</StackPanel>
<TextBlock FontSize="18" Text="Flyout ShowMode" />
<StackPanel>
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<WrapPanel Orientation="Horizontal">
<WrapPanel.Styles>
<Style Selector="Button">
<Setter Property="Margin" Value="4" />
</Style>
</WrapPanel.Styles>
<Button Content="ShowMode=Standard (default)">
<Button.Flyout>
<Flyout>
<StackPanel Width="200">
<TextBox />
<TextBlock Text="Standard ShowMode attempts to focus the Flyout when its opened" TextWrapping="Wrap"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="ShowMode=Transient">
<Button.Flyout>
<Flyout ShowMode="Transient">
<StackPanel Width="200">
<TextBox />
<TextBlock Text="Transient ShowMode does not focus the Flyout when opened" TextWrapping="Wrap"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="ShowMode=TransientWithDismissOnPointerMoveAway">
<Button.Flyout>
<Flyout ShowMode="TransientWithDismissOnPointerMoveAway">
<StackPanel Width="200">
<TextBox />
<TextBlock Text="Show in Transient mode (no focus), but closes the Flyout when the pointer moves away" TextWrapping="Wrap"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</WrapPanel>
</Border>
</StackPanel>
</StackPanel>
</ScrollViewer>
</UserControl>

81
samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs

@ -0,0 +1,81 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Xaml;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public class FlyoutsPage : UserControl
{
public FlyoutsPage()
{
InitializeComponent();
var afp = this.FindControl<Panel>("AttachedFlyoutPanel");
if (afp != null)
{
afp.DoubleTapped += Afp_DoubleTapped;
}
SetXamlTexts();
}
private void Afp_DoubleTapped(object sender, RoutedEventArgs e)
{
if (sender is Panel p)
{
FlyoutBase.ShowAttachedFlyout(p);
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void SetXamlTexts()
{
var bfxt = this.FindControl<TextBlock>("ButtonFlyoutXamlText");
bfxt.Text = "<Button Content=\"Click me!\">\n" +
" <Button.Flyout>\n" +
" <Flyout>\n" +
" <Panel Width=\"100\" Height=\"100\">\n" +
" <TextBlock Text=\"Flyout Content!\" />\n" +
" </Panel>\n" +
" </Flyout>\n" +
" </Button.Flyout>\n</Button>";
var mfxt = this.FindControl<TextBlock>("MenuFlyoutXamlText");
mfxt.Text = "<Button Content=\"Click me!\">\n" +
" <Button.Flyout>\n" +
" <MenuFlyout>\n" +
" <MenuItem Header=\"Item 1\">\n" +
" <MenuItem Header=\"Item 2\">\n" +
" </MenuFlyout>\n" +
" </Button.Flyout>\n</Button>";
var afxt = this.FindControl<TextBlock>("AttachedFlyoutXamlText");
afxt.Text = "<Panel Name=\"AttachedFlyoutPanel\">\n" +
" <FlyoutBase.AttachedFlyout>\n" +
" <Flyout>\n" +
" <Panel Height=\"100\">\n" +
" <TextBlock Text=\"Attached Flyout\" />\n" +
" </Panel>\n" +
" </Flyout>\n" +
" </FlyoutBase.AttachedFlyout>\n</Panel>" +
"\n\n In DoubleTapped handler:\n" +
"FlyoutBase.ShowAttachedFlyout(AttachedFlyoutPanel);";
var sfxt = this.FindControl<TextBlock>("SharedFlyoutXamlText");
sfxt.Text = "Declare a flyout in Resources:\n" +
"<Window.Resources>\n" +
" <Flyout x:Key=\"SharedFlyout\">\n" +
" <Panel Width=\"100\" Height=\"100\">\n" +
" <TextBlock Text=\"Flyout Content!\" />\n" +
" </Panel>\n" +
" </Flyout>\n</Window.Resources>\n\n" +
"Then attach the flyout where you want it:\n" +
"<Button Content=\"Launch Flyout here\" Flyout=\"{StaticResource SharedFlyout}\" />";
}
}
}

7
samples/ControlCatalog/Pages/ProgressBarPage.xaml

@ -15,6 +15,13 @@
<Slider Name="hprogress" Maximum="100" Value="40" /> <Slider Name="hprogress" Maximum="100" Value="40" />
<Slider Name="vprogress" Maximum="100" Value="60" /> <Slider Name="vprogress" Maximum="100" Value="60" />
</StackPanel> </StackPanel>
<StackPanel Spacing="10">
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True" />
<ProgressBar VerticalAlignment="Center" Value="5" Maximum="10" />
<ProgressBar VerticalAlignment="Center" Value="50" />
<ProgressBar VerticalAlignment="Center" Value="50" Minimum="25" Maximum="75" />
</StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

16
samples/ControlCatalog/Pages/ScreenPage.cs

@ -29,7 +29,7 @@ namespace ControlCatalog.Pages
var screens = w.Screens.All; var screens = w.Screens.All;
var scaling = ((IRenderRoot)w).RenderScaling; var scaling = ((IRenderRoot)w).RenderScaling;
var drawBrush = Brushes.Green; var drawBrush = Brushes.Black;
Pen p = new Pen(drawBrush); Pen p = new Pen(drawBrush);
if (screens != null) if (screens != null)
foreach (Screen screen in screens) foreach (Screen screen in screens)
@ -45,18 +45,16 @@ namespace ControlCatalog.Pages
screen.Bounds.Height / 10f); screen.Bounds.Height / 10f);
Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f, Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f,
screen.WorkingArea.Height / 10f); screen.WorkingArea.Height / 10f);
context.DrawRectangle(p, boundsRect); context.DrawRectangle(p, boundsRect);
context.DrawRectangle(p, workingAreaRect); context.DrawRectangle(p, workingAreaRect);
FormattedText text = new FormattedText()
{
Typeface = Typeface.Default
};
text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"; var text = new FormattedText() { Typeface = new Typeface("Arial"), FontSize = 18 };
text.Text = $"Bounds: {screen.Bounds.TopLeft} {screen.Bounds.Width}:{screen.Bounds.Height}";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height), text); context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height), text);
text.Text = $"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"; text.Text = $"WorkArea: {screen.WorkingArea.TopLeft} {screen.WorkingArea.Width}:{screen.WorkingArea.Height}";
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text); context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text);
text.Text = $"Scaling: {screen.PixelDensity * 100}%"; text.Text = $"Scaling: {screen.PixelDensity * 100}%";
@ -69,7 +67,7 @@ namespace ControlCatalog.Pages
context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text); context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text);
} }
context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10)); context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10));
} }
} }
} }

29
samples/ControlCatalog/Pages/SliderPage.xaml

@ -22,6 +22,20 @@
IsSnapToTickEnabled="True" IsSnapToTickEnabled="True"
Ticks="0,20,25,40,75,100" Ticks="0,20,25,40,75,100"
Width="300" /> Width="300" />
<Slider Name="SliderWithTooltip"
Value="0"
Minimum="0"
Maximum="100"
Width="300">
<Slider.Styles>
<Style Selector="Slider /template/ Thumb">
<Setter Property="ToolTip.Tip" Value="{Binding $parent[Slider].Value, Mode=OneWay, StringFormat='Value \{0:f\}'}" />
<Setter Property="ToolTip.Placement" Value="Top" />
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
</Style>
</Slider.Styles>
</Slider>
<Slider Value="0" <Slider Value="0"
Minimum="0" Minimum="0"
Maximum="100" Maximum="100"
@ -31,6 +45,12 @@
<sys:Exception /> <sys:Exception />
</DataValidationErrors.Error> </DataValidationErrors.Error>
</Slider> </Slider>
<Slider Value="0"
IsDirectionReversed="True"
Minimum="0"
Maximum="100"
TickFrequency="10"
Width="300" />
</StackPanel> </StackPanel>
<Slider Value="0" <Slider Value="0"
Minimum="0" Minimum="0"
@ -40,6 +60,15 @@
TickPlacement="Outside" TickPlacement="Outside"
TickFrequency="10" TickFrequency="10"
Height="300"/> Height="300"/>
<Slider Value="0"
IsDirectionReversed="True"
Minimum="0"
Maximum="100"
Orientation="Vertical"
IsSnapToTickEnabled="True"
TickPlacement="Outside"
TickFrequency="10"
Height="300"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

5
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -13,7 +13,7 @@
<StackPanel Orientation="Vertical" Spacing="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TextBox Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit." Width="200" /> <TextBox Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit." Width="200" />
<TextBox Width="200" Watermark="ReadOnly" IsReadOnly="True" Text="This is read only"/> <TextBox Width="200" Watermark="ReadOnly" IsReadOnly="True" Text="This is read only"/>
<TextBox Width="200" Watermark="Watermark" /> <TextBox Width="200" Watermark="Numeric Watermark" x:Name="numericWatermark"/>
<TextBox Width="200" <TextBox Width="200"
Watermark="Floating Watermark" Watermark="Floating Watermark"
UseFloatingWatermark="True" UseFloatingWatermark="True"
@ -64,5 +64,8 @@
<TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="/Assets/Fonts/SourceSansPro-*.ttf#Source Sans Pro"/> <TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="/Assets/Fonts/SourceSansPro-*.ttf#Source Sans Pro"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
<TextBox AcceptsReturn="True" TextWrapping="Wrap" Height="200" MaxWidth="400"
FontFamily="avares://ControlCatalog/Assets/Fonts#WenQuanYi Micro Hei"
Text="计算机科学(是系统性研究信息与计算的理论基础以及它们在计算机系统中如何实现与应用的实用技术的学科。它通常被形容为对那些创造、描述以及转换信息的算法处理的系统研究。计算机科学包含很多分支领域;有些强调特定结果的计算,比如计算机图形学;而有些是探討计算问题的性质,比如计算复杂性理论;还有一些领域專注于怎样实现计算,比如程式語言理論是研究描述计算的方法,而程式设计是应用特定的程式語言解决特定的计算问题,人机交互则是專注于怎样使计算机和计算变得有用、好用,以及随时随地为人所用。&#xD;&#xD;有时公众会误以为计算机科学就是解决计算机问题的事业(比如信息技术),或者只是与使用计算机的经验有关,如玩游戏、上网或者文字处理。其实计算机科学所关注的,不仅仅是去理解实现类似游戏、浏览器这些软件的程序的性质,更要通过现有的知识创造新的程序或者改进已有的程序。" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

6
samples/ControlCatalog/Pages/TextBoxPage.xaml.cs

@ -13,6 +13,12 @@ namespace ControlCatalog.Pages
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
this.Get<TextBox>("numericWatermark")
.TextInputOptionsQuery += (s, a) =>
{
a.ContentType = Avalonia.Input.TextInput.TextInputContentType.Number;
};
} }
} }
} }

27
samples/ControlCatalog/Pages/ToolTipPage.xaml

@ -6,7 +6,7 @@
<TextBlock Classes="h1">ToolTip</TextBlock> <TextBlock Classes="h1">ToolTip</TextBlock>
<TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock> <TextBlock Classes="h2">A control which pops up a hint when a control is hovered</TextBlock>
<Grid RowDefinitions="Auto,Auto" <Grid RowDefinitions="Auto,Auto,Auto"
ColumnDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center"> HorizontalAlignment="Center">
@ -38,6 +38,31 @@
</ToolTip.Tip> </ToolTip.Tip>
<TextBlock>ToolTip bottom placement</TextBlock> <TextBlock>ToolTip bottom placement</TextBlock>
</Border> </Border>
<Border Grid.Row="2"
Grid.ColumnSpan="2"
Background="{DynamicResource SystemAccentColor}"
Margin="5"
Padding="50"
ToolTip.Tip="Hello"
ToolTip.Placement="Top">
<Border.Styles>
<Style Selector="Border">
<Style.Animations>
<Animation Duration="0:0:2" IterationCount="Infinite">
<KeyFrame KeyTime="0:0:0">
<Setter Property="ToolTip.HorizontalOffset" Value="0" />
<Setter Property="ToolTip.VerticalOffset" Value="-50" />
</KeyFrame>
<KeyFrame KeyTime="0:0:2" >
<Setter Property="ToolTip.HorizontalOffset" Value="100" />
<Setter Property="ToolTip.VerticalOffset" Value="50" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Border.Styles>
<TextBlock>Moving offset</TextBlock>
</Border>
</Grid> </Grid>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

76
samples/ControlCatalog/Pages/ViewboxPage.xaml

@ -1,66 +1,36 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ViewboxPage"> x:Class="ControlCatalog.Pages.ViewboxPage">
<UserControl.Resources>
<StreamGeometry x:Key="Acorn">
F1 M 16.6309,18.6563C 17.1309,
8.15625 29.8809,14.1563 29.8809,
14.1563C 30.8809,11.1563 34.1308,
11.4063 34.1308,11.4063C 33.5,12
34.6309,13.1563 34.6309,13.1563C
32.1309,13.1562 31.1309,14.9062
31.1309,14.9062C 41.1309,23.9062
32.6309,27.9063 32.6309,27.9062C
24.6309,24.9063 21.1309,22.1562
16.6309,18.6563 Z M 16.6309,19.9063C
21.6309,24.1563 25.1309,26.1562
31.6309,28.6562C 31.6309,28.6562
26.3809,39.1562 18.3809,36.1563C
18.3809,36.1563 18,38 16.3809,36.9063C
15,36 16.3809,34.9063 16.3809,34.9063C
16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z
</StreamGeometry>
</UserControl.Resources>
<Grid RowDefinitions="Auto,*"> <Grid RowDefinitions="Auto,*,*">
<StackPanel Orientation="Vertical" Spacing="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Viewbox</TextBlock> <TextBlock Classes="h1">Viewbox</TextBlock>
<TextBlock Classes="h2">A control used to scale single child.</TextBlock> <TextBlock Classes="h2">A control used to scale single child.</TextBlock>
</StackPanel> </StackPanel>
<Grid ColumnDefinitions="Auto,*,*"
RowDefinitions="*,*,*,*"
Grid.Row="1" Margin="48"
MaxWidth="400">
<TextBlock Grid.Row="0" VerticalAlignment="Center">None</TextBlock>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Fill</TextBlock>
<TextBlock Grid.Row="2" VerticalAlignment="Center">Uniform</TextBlock>
<TextBlock Grid.Row="3" VerticalAlignment="Center">UniformToFill</TextBlock>
<Viewbox Grid.Row="0" Grid.Column="1" Stretch="None"> <Grid Grid.Row="1" ColumnDefinitions="*,Auto" HorizontalAlignment="Center" Margin="0,10,0,0">
<TextBlock>Hello World!</TextBlock>
</Viewbox>
<Viewbox Grid.Row="1" Grid.Column="1" Stretch="Fill">
<TextBlock>Hello World!</TextBlock>
</Viewbox>
<Viewbox Grid.Row="2" Grid.Column="1" Stretch="Uniform">
<TextBlock>Hello World!</TextBlock>
</Viewbox>
<Viewbox Grid.Row="3" Grid.Column="1" Stretch="UniformToFill">
<TextBlock>Hello World!</TextBlock>
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="2" Stretch="None"> <Border HorizontalAlignment="Center" Grid.Column="0" BorderThickness="1" BorderBrush="Orange" Width="200" Height="200">
<Path Fill="Blue" Data="{StaticResource Acorn}"/> <Border VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="0" BorderThickness="1" BorderBrush="CornflowerBlue" Width="{Binding #WidthSlider.Value}" Height="{Binding #HeightSlider.Value}" >
</Viewbox> <Viewbox
<Viewbox Grid.Row="1" Grid.Column="2" Stretch="Fill"> Stretch="{Binding #StretchSelector.SelectedItem}"
<Path Fill="Blue" Data="{StaticResource Acorn}"/> StretchDirection="{Binding #StretchDirectionSelector.SelectedItem}">
</Viewbox> <Ellipse Width="50" Height="50" Fill="CornflowerBlue" />
<Viewbox Grid.Row="2" Grid.Column="2" Stretch="Uniform"> </Viewbox>
<Path Fill="Blue" Data="{StaticResource Acorn}"/> </Border>
</Viewbox> </Border>
<Viewbox Grid.Row="3" Grid.Column="2" Stretch="UniformToFill">
<Path Fill="Blue" Data="{StaticResource Acorn}"/> <StackPanel HorizontalAlignment="Left" Orientation="Vertical" Grid.Column="1" Margin="8,0,0,0" Width="150">
</Viewbox> <TextBlock Text="Width" />
<Slider Minimum="10" Maximum="200" Value="100" x:Name="WidthSlider" TickFrequency="25" TickPlacement="TopLeft" />
<TextBlock Text="Height" />
<Slider Minimum="10" Maximum="200" Value="100" x:Name="HeightSlider" TickFrequency="25" TickPlacement="TopLeft" />
<TextBlock Text="Stretch" />
<ComboBox x:Name="StretchSelector" HorizontalAlignment="Stretch" Margin="0,0,0,2" />
<TextBlock Text="Stretch Direction" />
<ComboBox x:Name="StretchDirectionSelector" HorizontalAlignment="Stretch" />
</StackPanel>
</Grid> </Grid>
</Grid> </Grid>
</UserControl> </UserControl>

21
samples/ControlCatalog/Pages/ViewboxPage.xaml.cs

@ -1,5 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -7,7 +8,25 @@ namespace ControlCatalog.Pages
{ {
public ViewboxPage() public ViewboxPage()
{ {
this.InitializeComponent(); InitializeComponent();
var stretchSelector = this.FindControl<ComboBox>("StretchSelector");
stretchSelector.Items = new[]
{
Stretch.Uniform, Stretch.UniformToFill, Stretch.Fill, Stretch.None
};
stretchSelector.SelectedIndex = 0;
var stretchDirectionSelector = this.FindControl<ComboBox>("StretchDirectionSelector");
stretchDirectionSelector.Items = new[]
{
StretchDirection.Both, StretchDirection.DownOnly, StretchDirection.UpOnly
};
stretchDirectionSelector.SelectedIndex = 0;
} }
private void InitializeComponent() private void InitializeComponent()

78
samples/ControlCatalog/ViewModels/ContextFlyoutPageViewModel.cs

@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Reactive;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.VisualTree;
using MiniMvvm;
namespace ControlCatalog.ViewModels
{
public class ContextFlyoutPageViewModel
{
public Control View { get; set; }
public ContextFlyoutPageViewModel()
{
OpenCommand = MiniCommand.CreateFromTask(Open);
SaveCommand = MiniCommand.Create(Save);
OpenRecentCommand = MiniCommand.Create<string>(OpenRecent);
MenuItems = new[]
{
new MenuItemViewModel { Header = "_Open...", Command = OpenCommand },
new MenuItemViewModel { Header = "Save", Command = SaveCommand },
new MenuItemViewModel { Header = "-" },
new MenuItemViewModel
{
Header = "Recent",
Items = new[]
{
new MenuItemViewModel
{
Header = "File1.txt",
Command = OpenRecentCommand,
CommandParameter = @"c:\foo\File1.txt"
},
new MenuItemViewModel
{
Header = "File2.txt",
Command = OpenRecentCommand,
CommandParameter = @"c:\foo\File2.txt"
},
}
},
};
}
public IReadOnlyList<MenuItemViewModel> MenuItems { get; set; }
public MiniCommand OpenCommand { get; }
public MiniCommand SaveCommand { get; }
public MiniCommand OpenRecentCommand { get; }
public async Task Open()
{
var window = View?.GetVisualRoot() as Window;
if (window == null)
return;
var dialog = new OpenFileDialog();
var result = await dialog.ShowAsync(window);
if (result != null)
{
foreach (var path in result)
{
System.Diagnostics.Debug.WriteLine($"Opened: {path}");
}
}
}
public void Save()
{
System.Diagnostics.Debug.WriteLine("Save");
}
public void OpenRecent(string path)
{
System.Diagnostics.Debug.WriteLine($"Open recent: {path}");
}
}
}

44
samples/ControlCatalog/ViewModels/CursorPageViewModel.cs

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Input;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using MiniMvvm;
namespace ControlCatalog.ViewModels
{
public class CursorPageViewModel : ViewModelBase
{
public CursorPageViewModel()
{
StandardCursors = Enum.GetValues(typeof(StandardCursorType))
.Cast<StandardCursorType>()
.Select(x => new StandardCursorModel(x))
.ToList();
var loader = AvaloniaLocator.Current.GetService<IAssetLoader>();
var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png"));
var bitmap = new Bitmap(s);
CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16));
}
public IEnumerable<StandardCursorModel> StandardCursors { get; }
public Cursor CustomCursor { get; }
public class StandardCursorModel
{
public StandardCursorModel(StandardCursorType type)
{
Type = type;
Cursor = new Cursor(type);
}
public StandardCursorType Type { get; }
public Cursor Cursor { get; }
}
}
}

3
samples/RenderDemo/MainWindow.xaml

@ -57,6 +57,9 @@
<TabItem Header="LineBounds"> <TabItem Header="LineBounds">
<pages:LineBoundsPage /> <pages:LineBoundsPage />
</TabItem> </TabItem>
<TabItem Header="Path Measurement">
<pages:PathMeasurementPage />
</TabItem>
</TabControl> </TabControl>
</DockPanel> </DockPanel>
</Window> </Window>

7
samples/RenderDemo/Pages/AnimationsPage.xaml

@ -1,7 +1,8 @@
<UserControl <UserControl
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.Pages.AnimationsPage"> x:Class="RenderDemo.Pages.AnimationsPage"
MaxWidth="600">
<UserControl.Styles> <UserControl.Styles>
<Styles> <Styles>
<Styles.Resources> <Styles.Resources>
@ -167,8 +168,8 @@
<StackPanel.Clock> <StackPanel.Clock>
<Clock /> <Clock />
</StackPanel.Clock> </StackPanel.Clock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Spacing="20">
<TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock> <TextBlock VerticalAlignment="Center">Hover to activate Keyframe Animations.</TextBlock>
<Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" /> <Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" />
</StackPanel> </StackPanel>
<WrapPanel ClipToBounds="False"> <WrapPanel ClipToBounds="False">

89
samples/RenderDemo/Pages/PathMeasurementPage.cs

@ -0,0 +1,89 @@
using System;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Security.Cryptography;
using Avalonia;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Media.Immutable;
using Avalonia.Threading;
using Avalonia.Visuals.Media.Imaging;
namespace RenderDemo.Pages
{
public class PathMeasurementPage : Control
{
static PathMeasurementPage()
{
AffectsRender<PathMeasurementPage>(BoundsProperty);
}
private RenderTargetBitmap _bitmap;
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
_bitmap = new RenderTargetBitmap(new PixelSize(500, 500), new Vector(96, 96));
base.OnAttachedToLogicalTree(e);
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
_bitmap.Dispose();
_bitmap = null;
base.OnDetachedFromLogicalTree(e);
}
readonly IPen strokePen = new ImmutablePen(Brushes.DarkBlue, 10d, null, PenLineCap.Round, PenLineJoin.Round);
readonly IPen strokePen1 = new ImmutablePen(Brushes.Purple, 10d, null, PenLineCap.Round, PenLineJoin.Round);
readonly IPen strokePen2 = new ImmutablePen(Brushes.Green, 10d, null, PenLineCap.Round, PenLineJoin.Round);
readonly IPen strokePen3 = new ImmutablePen(Brushes.LightBlue, 10d, null, PenLineCap.Round, PenLineJoin.Round);
readonly IPen strokePen4 = new ImmutablePen(Brushes.Red, 1d, null, PenLineCap.Round, PenLineJoin.Round);
public override void Render(DrawingContext context)
{
using (var ctxi = _bitmap.CreateDrawingContext(null))
using (var bitmapCtx = new DrawingContext(ctxi, false))
{
ctxi.Clear(default);
var basePath = new PathGeometry();
using (var basePathCtx = basePath.Open())
{
basePathCtx.BeginFigure(new Point(20, 20), false);
basePathCtx.LineTo(new Point(400, 50));
basePathCtx.LineTo(new Point(80, 100));
basePathCtx.LineTo(new Point(300, 150));
basePathCtx.EndFigure(false);
}
bitmapCtx.DrawGeometry(null, strokePen, basePath);
var length = basePath.PlatformImpl.ContourLength;
if (basePath.PlatformImpl.TryGetSegment(length * 0.05, length * 0.2, true, out var dst1))
bitmapCtx.DrawGeometry(null, strokePen1, dst1);
if (basePath.PlatformImpl.TryGetSegment(length * 0.2, length * 0.8, true, out var dst2))
bitmapCtx.DrawGeometry(null, strokePen2, dst2);
if (basePath.PlatformImpl.TryGetSegment(length * 0.8, length * 0.95, true, out var dst3))
bitmapCtx.DrawGeometry(null, strokePen3, dst3);
var pathBounds = basePath.GetRenderBounds(strokePen);
bitmapCtx.DrawRectangle(null, strokePen4, pathBounds);
}
context.DrawImage(_bitmap,
new Rect(0, 0, 500, 500),
new Rect(0, 0, 500, 500));
base.Render(context);
}
}
}

65
samples/RenderDemo/Pages/TransitionsPage.xaml

@ -1,7 +1,8 @@
<UserControl <UserControl
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.Pages.TransitionsPage"> x:Class="RenderDemo.Pages.TransitionsPage"
MaxWidth="600">
<UserControl.Styles> <UserControl.Styles>
<Styles> <Styles>
<Styles.Resources> <Styles.Resources>
@ -90,6 +91,56 @@
<Setter Property="RenderTransform" Value="none" /> <Setter Property="RenderTransform" Value="none" />
</Style> </Style>
<Style Selector="Border.Rect7">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Height" Duration="0:0:0.5" />
</Transitions>
</Setter>
</Style>
<Style Selector="Border.Rect7:pointerover">
<Setter Property="Height" Value="50" />
</Style>
<Style Selector="Border.Rect8">
<Setter Property="Transitions">
<Transitions>
<CornerRadiusTransition Property="CornerRadius" Duration="0:0:0.5" />
</Transitions>
</Setter>
</Style>
<Style Selector="Border.Rect8:pointerover">
<Setter Property="CornerRadius" Value="50" />
</Style>
<Style Selector="Border.Rect9">
<Setter Property="Transitions">
<Transitions>
<ThicknessTransition Property="Padding" Duration="0:0:0.5" />
</Transitions>
</Setter>
</Style>
<Style Selector="Border.Rect9:pointerover">
<Setter Property="Padding" Value="10" />
</Style>
<Style Selector="Border.Shadow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BoxShadow" Value="inset 0 0 0 2 Red, -15 -15 Green"/>
<Setter Property="Transitions">
<Transitions>
<BoxShadowsTransition Property="BoxShadow" Duration="0:0:0.5" />
</Transitions>
</Setter>
</Style>
<Style Selector="Border.Shadow:pointerover">
<Setter Property="BoxShadow" Value="inset 30 30 20 30 Green, 20 40 20 10 Red"/>
</Style>
</Styles> </Styles>
</UserControl.Styles> </UserControl.Styles>
@ -98,8 +149,8 @@
<StackPanel.Clock> <StackPanel.Clock>
<Clock /> <Clock />
</StackPanel.Clock> </StackPanel.Clock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Spacing="20">
<TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock> <TextBlock VerticalAlignment="Center">Hover to activate Transitions.</TextBlock>
<Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" /> <Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" />
</StackPanel> </StackPanel>
<WrapPanel ClipToBounds="False"> <WrapPanel ClipToBounds="False">
@ -109,6 +160,12 @@
<Border Classes="Test Rect4" Background="Navy"/> <Border Classes="Test Rect4" Background="Navy"/>
<Border Classes="Test Rect5" Background="SeaGreen"/> <Border Classes="Test Rect5" Background="SeaGreen"/>
<Border Classes="Test Rect6" Background="Orange"/> <Border Classes="Test Rect6" Background="Orange"/>
<Border Classes="Test Rect7" Background="Gold"/>
<Border Classes="Test Rect8" Background="Gray" />
<Border Classes="Test Rect9" Background="Red" />
<Border Classes="Test Shadow" CornerRadius="10" Child="{x:Null}" />
<Border Classes="Test Shadow" CornerRadius="0 30 60 0" Child="{x:Null}" />
</WrapPanel> </WrapPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>

47
src/Android/Avalonia.Android/ActivityTracker.cs

@ -1,47 +0,0 @@
using Android.App;
using Android.OS;
namespace Avalonia.Android
{
internal class ActivityTracker : Java.Lang.Object, global::Android.App.Application.IActivityLifecycleCallbacks
{
public static Activity Current { get; private set; }
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
Current = activity;
}
public void OnActivityDestroyed(Activity activity)
{
if (Current == activity)
Current = null;
}
public void OnActivityPaused(Activity activity)
{
if (Current == activity)
Current = null;
}
public void OnActivityResumed(Activity activity)
{
Current = activity;
}
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
{
Current = activity;
}
public void OnActivityStarted(Activity activity)
{
Current = activity;
}
public void OnActivityStopped(Activity activity)
{
if (Current == activity)
Current = null;
}
}
}

96
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -0,0 +1,96 @@
using System;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Avalonia.Input;
using Avalonia.Input.TextInput;
namespace Avalonia.Android
{
class AndroidInputMethod<TView> : ITextInputMethodImpl
where TView: View, IInitEditorInfo
{
private readonly TView _host;
private readonly InputMethodManager _imm;
private IInputElement _inputElement;
public AndroidInputMethod(TView host)
{
if (host.OnCheckIsTextEditor() == false)
throw new InvalidOperationException("Host should return true from OnCheckIsTextEditor()");
_host = host;
_imm = host.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>();
_host.Focusable = true;
_host.FocusableInTouchMode = true;
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListner(_host));
}
public void Reset()
{
_imm.RestartInput(_host);
}
public void SetActive(bool active)
{
if (active)
{
_host.RequestFocus();
Reset();
_imm.ShowSoftInput(_host, ShowFlags.Implicit);
}
else
_imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None);
}
public void SetCursorRect(Rect rect)
{
}
public void SetOptions(TextInputOptionsQueryEventArgs options)
{
if (_inputElement != null)
{
_inputElement.PointerReleased -= RestoreSoftKeyboard;
}
_inputElement = options.Source as InputElement;
if (_inputElement == null)
{
_imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None);
}
_host.InitEditorInfo((outAttrs) =>
{
outAttrs.InputType = options.ContentType switch
{
TextInputContentType.Email => global::Android.Text.InputTypes.TextVariationEmailAddress,
TextInputContentType.Number => global::Android.Text.InputTypes.ClassNumber,
TextInputContentType.Password => global::Android.Text.InputTypes.TextVariationPassword,
TextInputContentType.Phone => global::Android.Text.InputTypes.ClassPhone,
TextInputContentType.Url => global::Android.Text.InputTypes.TextVariationUri,
_ => global::Android.Text.InputTypes.ClassText
};
if (options.AutoCapitalization)
{
outAttrs.InitialCapsMode = global::Android.Text.CapitalizationMode.Sentences;
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagCapSentences;
}
if (options.Multiline)
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
});
//_inputElement.PointerReleased += RestoreSoftKeyboard;
}
private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e)
{
_imm.ShowSoftInput(_host, ShowFlags.Implicit);
}
}
}

45
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -1,11 +1,13 @@
using System; using System;
using Avalonia.Android;
using Avalonia.Android.Platform; using Avalonia.Android.Platform;
using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.Input;
using Avalonia.Android.Platform.SkiaPlatform;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Shared.PlatformSupport; using Avalonia.Shared.PlatformSupport;
@ -17,7 +19,8 @@ namespace Avalonia
{ {
public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new() public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
{ {
builder.UseWindowingSubsystem(() => Android.AndroidPlatform.Initialize(builder.ApplicationType), "Android"); var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
builder.UseWindowingSubsystem(() => AndroidPlatform.Initialize(builder.ApplicationType, options), "Android");
builder.UseSkia(); builder.UseSkia();
return builder; return builder;
} }
@ -26,50 +29,42 @@ namespace Avalonia
namespace Avalonia.Android namespace Avalonia.Android
{ {
class AndroidPlatform : IPlatformSettings, IWindowingPlatform class AndroidPlatform : IPlatformSettings
{ {
public static readonly AndroidPlatform Instance = new AndroidPlatform(); public static readonly AndroidPlatform Instance = new AndroidPlatform();
public static AndroidPlatformOptions Options { get; private set; }
public Size DoubleClickSize => new Size(4, 4); public Size DoubleClickSize => new Size(4, 4);
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(200); public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(200);
public double RenderScalingFactor => _scalingFactor;
public double LayoutScalingFactor => _scalingFactor;
private readonly double _scalingFactor = 1;
public AndroidPlatform() public static void Initialize(Type appType, AndroidPlatformOptions options)
{ {
_scalingFactor = global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity; Options = options;
}
public static void Initialize(Type appType)
{
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IClipboard>().ToTransient<ClipboardImpl>() .Bind<IClipboard>().ToTransient<ClipboardImpl>()
.Bind<IStandardCursorFactory>().ToTransient<CursorFactory>() .Bind<ICursorFactory>().ToTransient<CursorFactory>()
.Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>() .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
.Bind<IPlatformSettings>().ToConstant(Instance) .Bind<IPlatformSettings>().ToConstant(Instance)
.Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface()) .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>() .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IWindowingPlatform>().ToConstant(Instance)
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60)) .Bind<IRenderTimer>().ToConstant(new ChoreographerTimer())
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>() .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IAssetLoader>().ToConstant(new AssetLoader(appType.Assembly)); .Bind<IAssetLoader>().ToConstant(new AssetLoader(appType.Assembly));
SkiaPlatform.Initialize(); SkiaPlatform.Initialize();
((global::Android.App.Application) global::Android.App.Application.Context.ApplicationContext)
.RegisterActivityLifecycleCallbacks(new ActivityTracker());
}
public IWindowImpl CreateWindow() if (options.UseGpu)
{ {
throw new NotSupportedException(); EglPlatformOpenGlInterface.TryInitialize();
}
} }
}
public IWindowImpl CreateEmbeddableWindow() public sealed class AndroidPlatformOptions
{ {
throw new NotSupportedException(); public bool UseDeferredRendering { get; set; } = true;
} public bool UseGpu { get; set; } = true;
} }
} }

15
src/Android/Avalonia.Android/AndroidThreadingInterface.cs

@ -1,25 +1,26 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Threading; using System.Threading;
using Android.OS; using Android.OS;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using App = Android.App.Application;
namespace Avalonia.Android namespace Avalonia.Android
{ {
class AndroidThreadingInterface : IPlatformThreadingInterface internal sealed class AndroidThreadingInterface : IPlatformThreadingInterface
{ {
private Handler _handler; private Handler _handler;
public AndroidThreadingInterface() public AndroidThreadingInterface()
{ {
_handler = new Handler(global::Android.App.Application.Context.MainLooper); _handler = new Handler(App.Context.MainLooper);
} }
public void RunLoop(CancellationToken cancellationToken) public void RunLoop(CancellationToken cancellationToken) => throw new NotSupportedException();
{
return;
}
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{ {
@ -57,7 +58,7 @@ namespace Avalonia.Android
}); });
} }
}, null, TimeSpan.Zero, interval); }, null, TimeSpan.Zero, interval);
return Disposable.Create(() => return Disposable.Create(() =>
{ {
lock (l) lock (l)

2
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -1,6 +1,6 @@
<Project Sdk="MSBuild.Sdk.Extras"> <Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup> <PropertyGroup>
<TargetFramework>monoandroid90</TargetFramework> <TargetFramework>monoandroid11.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

11
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -1,4 +1,3 @@
using Android.App; using Android.App;
using Android.OS; using Android.OS;
using Android.Views; using Android.Views;
@ -7,18 +6,15 @@ namespace Avalonia.Android
{ {
public abstract class AvaloniaActivity : Activity public abstract class AvaloniaActivity : Activity
{ {
internal AvaloniaView View; internal AvaloniaView View;
object _content; object _content;
protected override void OnCreate(Bundle savedInstanceState) protected override void OnCreate(Bundle savedInstanceState)
{ {
RequestWindowFeature(WindowFeatures.NoTitle);
View = new AvaloniaView(this); View = new AvaloniaView(this);
if(_content != null) if (_content != null)
View.Content = _content; View.Content = _content;
SetContentView(View); SetContentView(View);
TakeKeyEvents(true);
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
} }
@ -35,10 +31,5 @@ namespace Avalonia.Android
View.Content = value; View.Content = value;
} }
} }
public override bool DispatchKeyEvent(KeyEvent e)
{
return View.DispatchKeyEvent(e);
}
} }
} }

35
src/Android/Avalonia.Android/AvaloniaView.cs

@ -1,11 +1,12 @@
using System; using System;
using Android.Content; using Android.Content;
using Android.Runtime;
using Android.Views; using Android.Views;
using Android.Widget; using Android.Widget;
using Avalonia.Android.Platform.SkiaPlatform; using Avalonia.Android.Platform.SkiaPlatform;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Embedding; using Avalonia.Controls.Embedding;
using Avalonia.Platform; using Avalonia.Rendering;
namespace Avalonia.Android namespace Avalonia.Android
{ {
@ -14,6 +15,8 @@ namespace Avalonia.Android
private readonly EmbeddableControlRoot _root; private readonly EmbeddableControlRoot _root;
private readonly ViewImpl _view; private readonly ViewImpl _view;
private IDisposable? _timerSubscription;
public AvaloniaView(Context context) : base(context) public AvaloniaView(Context context) : base(context)
{ {
_view = new ViewImpl(context); _view = new ViewImpl(context);
@ -33,6 +36,36 @@ namespace Avalonia.Android
return _view.View.DispatchKeyEvent(e); return _view.View.DispatchKeyEvent(e);
} }
public override void OnVisibilityAggregated(bool isVisible)
{
base.OnVisibilityAggregated(isVisible);
OnVisibilityChanged(isVisible);
}
protected override void OnVisibilityChanged(View changedView, [GeneratedEnum] ViewStates visibility)
{
base.OnVisibilityChanged(changedView, visibility);
OnVisibilityChanged(visibility == ViewStates.Visible);
}
private void OnVisibilityChanged(bool isVisible)
{
if (isVisible)
{
if (AvaloniaLocator.Current.GetService<IRenderTimer>() is ChoreographerTimer timer)
{
_timerSubscription = timer.SubscribeView(this);
}
_root.Renderer.Start();
}
else
{
_root.Renderer.Stop();
_timerSubscription?.Dispose();
}
}
class ViewImpl : TopLevelImpl class ViewImpl : TopLevelImpl
{ {
public ViewImpl(Context context) : base(context) public ViewImpl(Context context) : base(context)

101
src/Android/Avalonia.Android/ChoreographerTimer.cs

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Android.OS;
using Android.Views;
using Avalonia.Rendering;
using Java.Lang;
namespace Avalonia.Android
{
internal sealed class ChoreographerTimer : Java.Lang.Object, IRenderTimer, Choreographer.IFrameCallback
{
private readonly object _lock = new object();
private readonly Thread _thread;
private readonly TaskCompletionSource<Choreographer> _choreographer = new TaskCompletionSource<Choreographer>();
private readonly ISet<AvaloniaView> _views = new HashSet<AvaloniaView>();
private Action<TimeSpan> _tick;
private int _count;
public ChoreographerTimer()
{
_thread = new Thread(Loop);
_thread.Start();
}
public event Action<TimeSpan> Tick
{
add
{
lock (_lock)
{
_tick += value;
_count++;
if (_count == 1)
{
_choreographer.Task.Result.PostFrameCallback(this);
}
}
}
remove
{
lock (_lock)
{
_tick -= value;
_count--;
}
}
}
internal IDisposable SubscribeView(AvaloniaView view)
{
lock (_lock)
{
_views.Add(view);
if (_views.Count == 1)
{
_choreographer.Task.Result.PostFrameCallback(this);
}
}
return Disposable.Create(
() =>
{
lock (_lock)
{
_views.Remove(view);
}
}
);
}
private void Loop()
{
Looper.Prepare();
_choreographer.SetResult(Choreographer.Instance);
Looper.Loop();
}
public void DoFrame(long frameTimeNanos)
{
_tick?.Invoke(TimeSpan.FromTicks(frameTimeNanos / 100));
lock (_lock)
{
if (_count > 0 && _views.Count > 0)
{
Choreographer.Instance.PostFrameCallback(this);
}
}
}
}
}

17
src/Android/Avalonia.Android/CursorFactory.cs

@ -1,12 +1,21 @@
using System;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Android namespace Avalonia.Android
{ {
internal class CursorFactory : IStandardCursorFactory internal class CursorFactory : ICursorFactory
{ {
public IPlatformHandle GetCursor(StandardCursorType cursorType) public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => CursorImpl.ZeroCursor;
=> new PlatformHandle(IntPtr.Zero, "ZeroCursor");
public ICursorImpl GetCursor(StandardCursorType cursorType) => CursorImpl.ZeroCursor;
private sealed class CursorImpl : ICursorImpl
{
public static CursorImpl ZeroCursor { get; } = new CursorImpl();
private CursorImpl() { }
public void Dispose() { }
}
} }
} }

12
src/Android/Avalonia.Android/IInitEditorInfo.cs

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using Android.Views.InputMethods;
namespace Avalonia.Android
{
interface IInitEditorInfo
{
void InitEditorInfo(Action<EditorInfo> init);
}
}

30
src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs

@ -0,0 +1,30 @@
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.Android.OpenGL
{
internal sealed class GlPlatformSurface : EglGlPlatformSurfaceBase
{
private readonly EglPlatformOpenGlInterface _egl;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
private GlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info)
{
_egl = egl;
_info = info;
}
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() =>
new GlRenderTarget(_egl, _info, _egl.CreateWindowSurface(_info.Handle), _info.Handle);
public static GlPlatformSurface TryCreate(IEglWindowGlPlatformSurfaceInfo info)
{
if (EglPlatformOpenGlInterface.TryCreate() is EglPlatformOpenGlInterface egl)
{
return new GlPlatformSurface(egl, info);
}
return null;
}
}
}

30
src/Android/Avalonia.Android/OpenGL/GlRenderTarget.cs

@ -0,0 +1,30 @@
using System;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.Android.OpenGL
{
internal sealed class GlRenderTarget : EglPlatformSurfaceRenderTargetBase, IGlPlatformSurfaceRenderTargetWithCorruptionInfo
{
private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
private readonly EglSurface _surface;
private readonly IntPtr _handle;
public GlRenderTarget(
EglPlatformOpenGlInterface egl,
EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info,
EglSurface surface,
IntPtr handle)
: base(egl)
{
_info = info;
_surface = surface;
_handle = handle;
}
public bool IsCorrupted => _handle != _info.Handle;
public override IGlPlatformSurfaceRenderingSession BeginDraw() => BeginDraw(_surface, _info);
}
}

4
src/Android/Avalonia.Android/Platform/ClipboardImpl.cs

@ -1,7 +1,11 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Android.Content; using Android.Content;
using Android.Runtime; using Android.Runtime;
using Android.Views; using Android.Views;
using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.Platform; using Avalonia.Platform;

14
src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs

@ -1,14 +0,0 @@
using Avalonia.Input;
namespace Avalonia.Android.Platform.Input
{
public class AndroidMouseDevice : MouseDevice
{
public static AndroidMouseDevice Instance { get; } = new AndroidMouseDevice();
public AndroidMouseDevice()
{
}
}
}

6
src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs

@ -10,7 +10,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{ {
private IntPtr _window; private IntPtr _window;
public AndroidFramebuffer(Surface surface) public AndroidFramebuffer(Surface surface, double scaling)
{ {
if(surface == null) if(surface == null)
throw new ArgumentNullException(nameof(surface)); throw new ArgumentNullException(nameof(surface));
@ -31,6 +31,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
RowBytes = buffer.stride * (Format == PixelFormat.Rgb565 ? 2 : 4); RowBytes = buffer.stride * (Format == PixelFormat.Rgb565 ? 2 : 4);
Address = buffer.bits; Address = buffer.bits;
Dpi = new Vector(96, 96) * scaling;
} }
public void Dispose() public void Dispose()
@ -44,7 +46,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public IntPtr Address { get; set; } public IntPtr Address { get; set; }
public PixelSize Size { get; } public PixelSize Size { get; }
public int RowBytes { get; } public int RowBytes { get; }
public Vector Dpi { get; } = new Vector(96, 96); public Vector Dpi { get; }
public PixelFormat Format { get; } public PixelFormat Format { get; }
[DllImport("android")] [DllImport("android")]

17
src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs

@ -0,0 +1,17 @@
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
namespace Avalonia.Android.Platform.SkiaPlatform
{
internal sealed class FramebufferManager : IFramebufferPlatformSurface
{
private readonly TopLevelImpl _topLevel;
public FramebufferManager(TopLevelImpl topLevel)
{
_topLevel = topLevel;
}
public ILockedFramebuffer Lock() => new AndroidFramebuffer(_topLevel.InternalView.Holder.Surface, _topLevel.RenderScaling);
}
}

4
src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs

@ -13,7 +13,7 @@ namespace Avalonia.Android
bool _invalidateQueued; bool _invalidateQueued;
readonly object _lock = new object(); readonly object _lock = new object();
private readonly Handler _handler; private readonly Handler _handler;
public InvalidationAwareSurfaceView(Context context) : base(context) public InvalidationAwareSurfaceView(Context context) : base(context)
{ {
@ -43,11 +43,13 @@ namespace Avalonia.Android
} }
} }
[Obsolete("deprecated")]
public override void Invalidate(global::Android.Graphics.Rect dirty) public override void Invalidate(global::Android.Graphics.Rect dirty)
{ {
Invalidate(); Invalidate();
} }
[Obsolete("deprecated")]
public override void Invalidate(int l, int t, int r, int b) public override void Invalidate(int l, int t, int r, int b)
{ {
Invalidate(); Invalidate();

127
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -2,71 +2,60 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Android.Content; using Android.Content;
using Android.Graphics; using Android.Graphics;
using Android.Runtime;
using Android.Views; using Android.Views;
using Avalonia.Android.Platform.Input; using Android.Views.InputMethods;
using Avalonia.Android.OpenGL;
using Avalonia.Android.Platform.Specific; using Avalonia.Android.Platform.Specific;
using Avalonia.Android.Platform.Specific.Helpers; using Avalonia.Android.Platform.Specific.Helpers;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
namespace Avalonia.Android.Platform.SkiaPlatform namespace Avalonia.Android.Platform.SkiaPlatform
{ {
class TopLevelImpl : IAndroidView, ITopLevelImpl, IFramebufferPlatformSurface class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithTextInputMethod
{ {
private readonly IGlPlatformSurface _gl;
private readonly IFramebufferPlatformSurface _framebuffer;
private readonly AndroidKeyboardEventsHelper<TopLevelImpl> _keyboardHelper; private readonly AndroidKeyboardEventsHelper<TopLevelImpl> _keyboardHelper;
private readonly AndroidTouchEventsHelper<TopLevelImpl> _touchHelper; private readonly AndroidTouchEventsHelper<TopLevelImpl> _touchHelper;
private readonly ITextInputMethodImpl _textInputMethod;
private ViewImpl _view; private ViewImpl _view;
public TopLevelImpl(Context context, bool placeOnTop = false) public TopLevelImpl(Context context, bool placeOnTop = false)
{ {
_view = new ViewImpl(context, this, placeOnTop); _view = new ViewImpl(context, this, placeOnTop);
_textInputMethod = new AndroidInputMethod<ViewImpl>(_view);
_keyboardHelper = new AndroidKeyboardEventsHelper<TopLevelImpl>(this); _keyboardHelper = new AndroidKeyboardEventsHelper<TopLevelImpl>(this);
_touchHelper = new AndroidTouchEventsHelper<TopLevelImpl>(this, () => InputRoot, _touchHelper = new AndroidTouchEventsHelper<TopLevelImpl>(this, () => InputRoot,
p => GetAvaloniaPointFromEvent(p)); GetAvaloniaPointFromEvent);
Surfaces = new object[] { this };
MaxClientSize = new Size(_view.Resources.DisplayMetrics.WidthPixels,
_view.Resources.DisplayMetrics.HeightPixels);
}
_gl = GlPlatformSurface.TryCreate(this);
_framebuffer = new FramebufferManager(this);
private bool _handleEvents; RenderScaling = (int)_view.Resources.DisplayMetrics.Density;
public bool HandleEvents MaxClientSize = new PixelSize(_view.Resources.DisplayMetrics.WidthPixels,
{ _view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling);
get { return _handleEvents; }
set
{
_handleEvents = value;
_keyboardHelper.HandleEvents = _handleEvents;
}
} }
public virtual Point GetAvaloniaPointFromEvent(MotionEvent e) => new Point(e.GetX(), e.GetY()); public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) =>
new Point(e.GetX(pointerIndex), e.GetY(pointerIndex)) / RenderScaling;
public IInputRoot InputRoot { get; private set; } public IInputRoot InputRoot { get; private set; }
public virtual Size ClientSize public virtual Size ClientSize => Size.ToSize(RenderScaling);
{
get
{
if (_view == null)
return new Size(0, 0);
return new Size(_view.Width, _view.Height);
}
set
{
}
}
public IMouseDevice MouseDevice => AndroidMouseDevice.Instance; public IMouseDevice MouseDevice { get; } = new MouseDevice();
public Action Closed { get; set; } public Action Closed { get; set; }
@ -82,20 +71,22 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public View View => _view; public View View => _view;
internal InvalidationAwareSurfaceView InternalView => _view;
public IPlatformHandle Handle => _view; public IPlatformHandle Handle => _view;
public IEnumerable<object> Surfaces { get; } public IEnumerable<object> Surfaces => new object[] { _gl, _framebuffer };
public IRenderer CreateRenderer(IRenderRoot root) public IRenderer CreateRenderer(IRenderRoot root) =>
{ AndroidPlatform.Options.UseDeferredRendering
return new ImmediateRenderer(root); ? new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>()) { RenderOnlyOnRenderThread = true }
} : new ImmediateRenderer(root);
public virtual void Hide() public virtual void Hide()
{ {
_view.Visibility = ViewStates.Invisible; _view.Visibility = ViewStates.Invisible;
} }
public void Invalidate(Rect rect) public void Invalidate(Rect rect)
{ {
if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate(); if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate();
@ -103,15 +94,15 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Point PointToClient(PixelPoint point) public Point PointToClient(PixelPoint point)
{ {
return point.ToPoint(1); return point.ToPoint(RenderScaling);
} }
public PixelPoint PointToScreen(Point point) public PixelPoint PointToScreen(Point point)
{ {
return PixelPoint.FromPoint(point, 1); return PixelPoint.FromPoint(point, RenderScaling);
} }
public void SetCursor(IPlatformHandle cursor) public void SetCursor(ICursorImpl cursor)
{ {
//still not implemented //still not implemented
} }
@ -126,7 +117,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Visibility = ViewStates.Visible; _view.Visibility = ViewStates.Visible;
} }
public double RenderScaling => 1; public double RenderScaling { get; }
void Draw() void Draw()
{ {
@ -144,7 +135,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
Resized?.Invoke(size); Resized?.Invoke(size);
} }
class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo
{ {
private readonly TopLevelImpl _tl; private readonly TopLevelImpl _tl;
private Size _oldSize; private Size _oldSize;
@ -181,7 +172,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
void ISurfaceHolderCallback.SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height) void ISurfaceHolderCallback.SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height)
{ {
var newSize = new Size(width, height); var newSize = new PixelSize(width, height).ToSize(_tl.RenderScaling);
if (newSize != _oldSize) if (newSize != _oldSize)
{ {
@ -191,12 +182,50 @@ namespace Avalonia.Android.Platform.SkiaPlatform
base.SurfaceChanged(holder, format, width, height); base.SurfaceChanged(holder, format, width, height);
} }
public sealed override bool OnCheckIsTextEditor()
{
return true;
}
private Action<EditorInfo> _initEditorInfo;
public void InitEditorInfo(Action<EditorInfo> init)
{
_initEditorInfo = init;
}
public sealed override IInputConnection OnCreateInputConnection(EditorInfo outAttrs)
{
if (_initEditorInfo != null)
_initEditorInfo(outAttrs);
return base.OnCreateInputConnection(outAttrs);
}
} }
public IPopupImpl CreatePopup() => null; public IPopupImpl CreatePopup() => null;
public Action LostFocus { get; set; } public Action LostFocus { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1);
IntPtr EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo.Handle =>
AndroidFramebuffer.ANativeWindow_fromSurface(JNIEnv.Handle, _view.Holder.Surface.Handle);
ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface); public PixelSize Size => new PixelSize(_view.Holder.SurfaceFrame.Width(), _view.Holder.SurfaceFrame.Height());
public double Scaling => RenderScaling;
public ITextInputMethodImpl TextInputMethod => _textInputMethod;
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
{
throw new NotImplementedException();
}
} }
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save