Browse Source

Merge branch 'master' into aot

pull/7558/head
Max Katz 5 years ago
committed by GitHub
parent
commit
f3977e9a91
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      Avalonia.sln
  2. 1
      Directory.Build.props
  3. 9
      azure-pipelines.yml
  4. 16
      build/AndroidWorkarounds.props
  5. 2
      build/SharedVersion.props
  6. 11
      dirs.proj
  7. 1
      global.json
  8. 5
      nukebuild/Build.cs
  9. 19
      samples/ControlCatalog.Android/Assets/AboutAssets.txt
  10. 183
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  11. 5
      samples/ControlCatalog.Android/MainActivity.cs
  12. 7
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  13. 30
      samples/ControlCatalog.Android/Properties/AssemblyInfo.cs
  14. 101
      samples/ControlCatalog.Android/Resources/Resource.Designer.cs
  15. 2
      samples/ControlCatalog.Android/Resources/values/styles.xml
  16. 2
      samples/ControlCatalog.Android/SplashActivity.cs
  17. 1
      samples/ControlCatalog.Web/ControlCatalog.Web.csproj
  18. 7
      samples/ControlCatalog/MainView.xaml
  19. 45
      samples/ControlCatalog/Pages/ButtonPage.xaml
  20. 224
      samples/ControlCatalog/Pages/ButtonsPage.xaml
  21. 4
      samples/ControlCatalog/Pages/ButtonsPage.xaml.cs
  22. 23
      samples/ControlCatalog/Pages/ClipboardPage.xaml
  23. 77
      samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
  24. 8
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  25. 4
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  26. 2
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  27. 2
      samples/ControlCatalog/Pages/ToggleSwitchPage.xaml
  28. 4
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  29. 9
      src/Android/Avalonia.Android/AndroidPlatform.cs
  30. 14
      src/Android/Avalonia.Android/AppBuilder.cs
  31. 17
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  32. 36
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  33. 11
      src/Android/Avalonia.Android/AvaloniaViewModel.cs
  34. 50
      src/Android/Avalonia.Android/Resources/AboutResources.txt
  35. 6
      src/Android/Avalonia.Android/Resources/Values/Strings.xml
  36. 4
      src/Android/Avalonia.Android/SoftKeyboardListener.cs
  37. 19
      src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt
  38. 172
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  39. 2
      src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
  40. 6
      src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml
  41. 30
      src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs
  42. 79
      src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs
  43. 11
      src/Android/Avalonia.AndroidTestApplication/app.config
  44. 6
      src/Avalonia.Controls/Button.cs
  45. 3
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  46. 20
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  47. 5
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  48. 2
      src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
  49. 23
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  50. 5
      src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
  51. 2
      src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
  52. 2
      src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
  53. 2
      src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
  54. 2
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  55. 2
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  56. 199
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  57. 2
      src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
  58. 20
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  59. 37
      src/Avalonia.Controls/Primitives/TextSearch.cs
  60. 485
      src/Avalonia.Controls/SplitButton/SplitButton.cs
  61. 125
      src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs
  62. 7
      src/Avalonia.Controls/TextBlock.cs
  63. 25
      src/Avalonia.Controls/TextBox.cs
  64. 96
      src/Avalonia.Controls/TransitioningContentControl.cs
  65. 2
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  66. 6
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
  67. 1
      src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
  68. 7
      src/Avalonia.Input/ICommandSource.cs
  69. 10
      src/Avalonia.Input/MouseDevice.cs
  70. 15
      src/Avalonia.Input/PointerEventArgs.cs
  71. 58
      src/Avalonia.Input/Raw/RawPointerEventArgs.cs
  72. 53
      src/Avalonia.MicroCom/CallbackBase.cs
  73. 15
      src/Avalonia.MicroCom/MicroComRuntime.cs
  74. 6
      src/Avalonia.MicroCom/MicroComShadow.cs
  75. 2
      src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs
  76. 2
      src/Avalonia.Native/AvaloniaNativeDragSource.cs
  77. 2
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  78. 49
      src/Avalonia.Native/CallbackBase.cs
  79. 2
      src/Avalonia.Native/DeferredFramebuffer.cs
  80. 2
      src/Avalonia.Native/IAvnMenu.cs
  81. 2
      src/Avalonia.Native/MenuActionCallback.cs
  82. 4
      src/Avalonia.Native/PlatformThreadingInterface.cs
  83. 2
      src/Avalonia.Native/PredicateCallback.cs
  84. 2
      src/Avalonia.Native/SystemDialogs.cs
  85. 2
      src/Avalonia.Native/WindowImplBase.cs
  86. 2
      src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs
  87. 1
      src/Avalonia.ReactiveUI/ReactiveUserControl.cs
  88. 1
      src/Avalonia.ReactiveUI/TransitioningContentControl.cs
  89. 271
      src/Avalonia.Themes.Default/Controls/SplitButton.xaml
  90. 20
      src/Avalonia.Themes.Default/Controls/TransitioningContentControl.xaml
  91. 2
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  92. 27
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
  93. 27
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
  94. 2
      src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml
  95. 2
      src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
  96. 247
      src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml
  97. 20
      src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml
  98. 6
      src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
  99. 2
      src/Avalonia.Visuals/Animation/PageSlide.cs
  100. 26
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

9
Avalonia.sln

@ -77,7 +77,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}"
EndProject
@ -95,7 +95,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}"
EndProject
@ -107,7 +107,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interop", "Interop", "{A0CC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RenderDemo", "samples\RenderDemo\RenderDemo.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}"
EndProject
@ -115,7 +115,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
build\AndroidWorkarounds.props = build\AndroidWorkarounds.props
build\ApiDiff.props = build\ApiDiff.props
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
@ -1106,7 +1105,7 @@ Global
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.Build.0 = AppStore|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.ActiveCfg = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.Build.0 = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator

1
Directory.Build.props

@ -4,5 +4,6 @@
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<!-- https://github.com/dotnet/msbuild/issues/2661 -->
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
</PropertyGroup>
</Project>

9
azure-pipelines.yml

@ -1,6 +1,3 @@
variables:
MSBuildEnableWorkloadResolver: 'false'
jobs:
- job: GetPRNumber
@ -146,6 +143,12 @@ jobs:
inputs:
version: 6.0.100
- task: CmdLine@2
displayName: 'Install Workloads'
inputs:
script: |
dotnet workload install android
- task: CmdLine@2
displayName: 'Install Nuke'
inputs:

16
build/AndroidWorkarounds.props

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

2
build/SharedVersion.props

@ -11,7 +11,7 @@
<LangVersion>latest</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon>
<PackageDescription>Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.</PackageDescription>
<PackageDescription>Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOS and with experimental support for Android, iOS and WebAssembly.</PackageDescription>
<PackageTags>avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin</PackageTags>
<PackageReleaseNotes>https://github.com/AvaloniaUI/Avalonia/releases</PackageReleaseNotes>
<RepositoryType>git</RepositoryType>

11
dirs.proj

@ -1,5 +1,7 @@
<Project Sdk="Microsoft.Build.Traversal">
<ItemGroup>
<!-- Build Avalonia.Build.Tasks first because everything depends on it -->
<ProjectReference Include="src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" />
<ProjectReference Include="src/**/*.*proj" />
<ProjectReference Include="samples/**/*.*proj" />
<ProjectReference Include="tests/**/*.*proj" />
@ -8,11 +10,8 @@
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
<ProjectReference Remove="tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj" />
</ItemGroup>
<!--<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">-->
<ItemGroup>
<ProjectReference Remove="src/Android/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Android/ControlCatalog.Android.csproj" />
<ProjectReference Remove="src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\iOS')">
<ProjectReference Remove="src/iOS/**/*.*proj" />
@ -23,6 +22,10 @@
<ProjectReference Remove="samples/interop/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" />
</ItemGroup>
<!-- Build android projects only on Windows, where we have installed android workload -->
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows'))">
<ProjectReference Remove="src/Android/**/*.*proj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SlnGen" Version="2.0.40" PrivateAssets="all" />

1
global.json

@ -4,6 +4,7 @@
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",
"Xamarin.Legacy.Sdk": "0.1.2-alpha6",
"MSBuild.Sdk.Extras": "3.0.22",
"AggregatePackage.NuGet.Sdk" : "0.1.12"
}

5
nukebuild/Build.cs

@ -87,7 +87,8 @@ partial class Build : NukeBuild
Console.WriteLine(preamble);
Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
}
ExecWait("dotnet version:", "dotnet", "--version");
ExecWait("dotnet version:", "dotnet", "--info");
ExecWait("dotnet workloads:", "dotnet", "workload list");
}
IReadOnlyCollection<Output> MsBuildCommon(
@ -99,7 +100,7 @@ partial class Build : NukeBuild
// This is required for VS2019 image on Azure Pipelines
.When(Parameters.IsRunningOnWindows &&
Parameters.IsRunningOnAzure, _ => _
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64")))
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_11_X64")))
.AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", true)
.SetProcessToolPath(MsBuildExe.Value)

19
samples/ControlCatalog.Android/Assets/AboutAssets.txt

@ -1,19 +0,0 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

183
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -1,165 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{29132311-1848-4FD6-AE0C-4FF841151BD3}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ControlCatalog.Android</RootNamespace>
<AssemblyName>ControlCatalog.Android</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ApplicationId>com.Avalonia.ControlCatalog</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<EnableProguard>False</EnableProguard>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>Full</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86;x86_64</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<EnableProguard>False</EnableProguard>
<DebugSymbols>False</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SplashActivity.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\values\styles.xml" />
<None Remove="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="..\..\build\Assets\Icon.png">
<Link>Resources\drawable\Icon.png</Link>
</AndroidResource>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<DebugSymbols>True</DebugSymbols>
<RunAOTCompilation>False</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
<RunAOTCompilation>False</RunAOTCompilation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj">
<Project>{7B92AF71-6287-4693-9DCB-BD5B6E927E23}</Project>
<Name>Avalonia.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj">
<Project>{c42d2fc1-a531-4ed4-84b9-89aec7c962fc}</Project>
<Name>Avalonia.Themes.Fluent</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
<Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
<Name>Avalonia.Markup</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj">
<Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>
<Name>ControlCatalog</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\build\AndroidWorkarounds.props" />
<Import Project="..\..\build\LegacyProject.targets" />
</Project>

5
samples/ControlCatalog.Android/MainActivity.cs

@ -5,10 +5,10 @@ using Avalonia.Android;
namespace ControlCatalog.Android
{
[Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)]
[Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
public class MainActivity : AvaloniaActivity
{
protected override void OnCreate(Bundle savedInstanceState)
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
@ -16,4 +16,3 @@ namespace ControlCatalog.Android
}
}
}

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

@ -1,5 +1,4 @@
<?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">
<uses-sdk android:targetSdkVersion="30" />
<application android:label="ControlCatalog.Android"></application>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
<application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
</manifest>

30
samples/ControlCatalog.Android/Properties/AssemblyInfo.cs

@ -1,30 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ControlCatalog.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ControlCatalog.Android")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

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

@ -1,101 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("ControlCatalog.Android.Resource", IsApplication=true)]
namespace ControlCatalog.Android
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Color
{
// aapt resource value: 0x7F010000
public const int splash_background = 2130771968;
static Color()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Color()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F020000
public const int Icon = 2130837504;
// aapt resource value: 0x7F020001
public const int splash_screen = 2130837505;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class Style
{
// aapt resource value: 0x7F030000
public const int MyTheme = 2130903040;
// aapt resource value: 0x7F030001
public const int MyTheme_NoActionBar = 2130903041;
// aapt resource value: 0x7F030002
public const int MyTheme_Splash = 2130903042;
static Style()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Style()
{
}
}
}
}
#pragma warning restore 1591

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

@ -4,7 +4,7 @@
<style name="MyTheme">
</style>
<style name="MyTheme.NoActionBar">
<style name="MyTheme.NoActionBar" parent="@style/Theme.AppCompat.NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>

2
samples/ControlCatalog.Android/SplashActivity.cs

@ -10,7 +10,7 @@ namespace ControlCatalog.Android
[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
}

1
samples/ControlCatalog.Web/ControlCatalog.Web.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
<Nullable>enable</Nullable>
<WasmBuildNative>True</WasmBuildNative>
</PropertyGroup>

7
samples/ControlCatalog/MainView.xaml

@ -22,8 +22,8 @@
<TabItem Header="Border">
<pages:BorderPage />
</TabItem>
<TabItem Header="Button">
<pages:ButtonPage />
<TabItem Header="Buttons">
<pages:ButtonsPage />
</TabItem>
<TabItem Header="ButtonSpinner">
<pages:ButtonSpinnerPage />
@ -40,6 +40,9 @@
<TabItem Header="CheckBox">
<pages:CheckBoxPage />
</TabItem>
<TabItem Header="Clipboard">
<pages:ClipboardPage />
</TabItem>
<TabItem Header="ComboBox">
<pages:ComboBoxPage />
</TabItem>

45
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -1,45 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ButtonPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A button control</TextBlock>
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
<StackPanel Orientation="Vertical" Spacing="8" Width="200">
<Button>Standard _XAML Button</Button>
<Button Foreground="White">Foreground</Button>
<Button Background="{DynamicResource SystemAccentColor}">Background</Button>
<Button IsEnabled="False">Disabled</Button>
<Button Content="Re-themed">
<Button.Styles>
<Style>
<Style.Resources>
<SolidColorBrush x:Key="ThemeBorderMidBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighBrush">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBorderBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackground">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPressed">OrangeRed</SolidColorBrush>
</Style.Resources>
</Style>
</Button.Styles>
</Button>
<RepeatButton Name="RepeatButton">
<TextBlock Name="RepeatButtonTextBlock" Text="Repeat Button: 0" />
</RepeatButton>
<ToggleButton Content="Toggle Button"/>
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8" Width="150">
<Button BorderThickness="0">No Border</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}">Border Color</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="4">Thick Border</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="4" IsEnabled="False">Disabled</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}" KeyboardNavigation.IsTabStop="False">IsTabStop=False</Button>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

224
samples/ControlCatalog/Pages/ButtonsPage.xaml

@ -0,0 +1,224 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ButtonsPage">
<UserControl.Resources>
<MenuFlyout x:Key="SharedMenuFlyout"
Placement="Bottom">
<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>
</UserControl.Resources>
<UserControl.Styles >
<Style Selector="Border.header-border">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Gray" Opacity="0.5" />
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="0.5" />
<Setter Property="CornerRadius" Value="5,5,0,0" />
<Setter Property="MaxWidth" Value="450" />
<Setter Property="Padding" Value="10" />
</Style>
<Style Selector="TextBlock.header">
<Setter Property="FontSize" Value="18" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="Border.thin">
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="0.5" />
<Setter Property="CornerRadius" Value="0,0,5,5" />
<Setter Property="Margin" Value="0,0,0,15" />
</Style>
</UserControl.Styles>
<!-- Styles and overall page design based on AcrylicPage -->
<StackPanel Orientation="Vertical"
Width="450">
<!-- Button -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="Button" Classes="header" />
<TextBlock TextWrapping="Wrap">A standard button control</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="10">
<StackPanel Orientation="Vertical"
Spacing="8"
Width="200">
<Button>Standard _XAML Button</Button>
<Button Foreground="White">Foreground</Button>
<Button Background="{DynamicResource SystemAccentColor}">Background</Button>
<Button IsEnabled="False">Disabled</Button>
<Button Content="Re-themed">
<Button.Styles>
<Style>
<Style.Resources>
<SolidColorBrush x:Key="ThemeBorderMidBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighBrush">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBorderBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackground">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="ButtonBackgroundPressed">OrangeRed</SolidColorBrush>
<SolidColorBrush x:Key="ButtonForeground">White</SolidColorBrush>
<SolidColorBrush x:Key="ButtonForegroundPointerOver">Black</SolidColorBrush>
<SolidColorBrush x:Key="ButtonForegroundPressed">Black</SolidColorBrush>
</Style.Resources>
</Style>
</Button.Styles>
</Button>
</StackPanel>
<StackPanel Orientation="Vertical"
Spacing="8"
Width="200">
<Button BorderThickness="0">No Border</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}">Border Color</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="4">Thick Border</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="4"
IsEnabled="False">Disabled</Button>
<Button BorderBrush="{DynamicResource SystemAccentColor}"
KeyboardNavigation.IsTabStop="False">IsTabStop=False</Button>
</StackPanel>
</StackPanel>
</Border>
<!-- ToggleButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="ToggleButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button control with multiple states: checked, unchecked or indeterminate.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<ToggleButton Content="Toggle Button" />
</StackPanel>
</Border>
<!-- RepeatButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="RepeatButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button control that raises its Click event repeatedly when it is pressed and held.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<RepeatButton Name="RepeatButton">
<TextBlock Name="RepeatButtonTextBlock"
Text="Repeat Button: 0" />
</RepeatButton>
</StackPanel>
</Border>
<!-- SplitButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="SplitButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button with primary and secondary parts that can each be pressed separately. The primary part behaves like a Button and the secondary part opens a flyout.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<SplitButton Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Content" />
</SplitButton>
<SplitButton IsEnabled="False"
Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Disabled" />
</SplitButton>
<SplitButton Flyout="{StaticResource SharedMenuFlyout}"
Content="Re-themed">
<SplitButton.Styles>
<Style>
<Style.Resources>
<x:Double x:Key="SplitButtonSeparatorWidth">1.5</x:Double>
<Thickness x:Key="SplitButtonBorderThemeThickness">2</Thickness>
<SolidColorBrush x:Key="SplitButtonBorderBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBorderBrushPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBorderBrushPressed">Red</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBackground">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBackgroundPointerOver">Red</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonBackgroundPressed">OrangeRed</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonForeground">White</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonForegroundPointerOver">Black</SolidColorBrush>
<SolidColorBrush x:Key="SplitButtonForegroundPressed">Black</SolidColorBrush>
</Style.Resources>
</Style>
</SplitButton.Styles>
</SplitButton>
</StackPanel>
</Border>
<!-- ToggleSplitButton -->
<Border Classes="header-border">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="ToggleSplitButton"
Classes="header" />
<TextBlock TextWrapping="Wrap">A button with primary and secondary parts that can each be pressed separately. The primary part behaves like a ToggleButton with two states and the secondary part opens a flyout.</TextBlock>
</StackPanel>
</Border>
<Border Classes="thin"
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<ToggleSplitButton Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Content" />
</ToggleSplitButton>
<ToggleSplitButton IsChecked="True"
Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Content" />
</ToggleSplitButton>
<ToggleSplitButton IsChecked="True"
IsEnabled="False"
Flyout="{StaticResource SharedMenuFlyout}">
<TextBlock Text="Disabled" />
</ToggleSplitButton>
</StackPanel>
</Border>
</StackPanel>
</UserControl>

4
samples/ControlCatalog/Pages/ButtonPage.xaml.cs → samples/ControlCatalog/Pages/ButtonsPage.xaml.cs

@ -3,11 +3,11 @@ using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class ButtonPage : UserControl
public class ButtonsPage : UserControl
{
private int repeatButtonClickCount = 0;
public ButtonPage()
public ButtonsPage()
{
InitializeComponent();

23
samples/ControlCatalog/Pages/ClipboardPage.xaml

@ -0,0 +1,23 @@
<UserControl x:Class="ControlCatalog.Pages.ClipboardPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">Example of ClipboardPage capabilities</TextBlock>
<Button Click="CopyText" Content="Copy text to clipboard" />
<Button Click="PasteText" Content="Paste text from clipboard" />
<Button Click="CopyTextDataObject" Content="Copy text to clipboard (data object)" />
<Button Click="PasteTextDataObject" Content="Paste text from clipboard (data object)" />
<Button Click="CopyFilesDataObject" Content="Copy files to clipboard (data object)" />
<Button Click="PasteFilesDataObject" Content="Paste files from clipboard (data object)" />
<Button Click="GetFormats" Content="Get clipboard formats" />
<Button Click="Clear" Content="Clear clipboard" />
<TextBox x:Name="ClipboardContent"
MinHeight="100"
AcceptsReturn="True"
Watermark="Text to copy of file names per line" />
</StackPanel>
</UserControl>

77
samples/ControlCatalog/Pages/ClipboardPage.xaml.cs

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public partial class ClipboardPage : UserControl
{
public ClipboardPage()
{
InitializeComponent();
}
private TextBox ClipboardContent => this.Get<TextBox>("ClipboardContent");
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private async void CopyText(object sender, RoutedEventArgs args)
{
await Application.Current.Clipboard.SetTextAsync(ClipboardContent.Text);
}
private async void PasteText(object sender, RoutedEventArgs args)
{
ClipboardContent.Text = await Application.Current.Clipboard.GetTextAsync();
}
private async void CopyTextDataObject(object sender, RoutedEventArgs args)
{
var dataObject = new DataObject();
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
private async void PasteTextDataObject(object sender, RoutedEventArgs args)
{
ClipboardContent.Text = await Application.Current.Clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty;
}
private async void CopyFilesDataObject(object sender, RoutedEventArgs args)
{
var files = ClipboardContent.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
if (files.Length == 0)
{
return;
}
var dataObject = new DataObject();
dataObject.Set(DataFormats.FileNames, files);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
private async void PasteFilesDataObject(object sender, RoutedEventArgs args)
{
var fiels = await Application.Current.Clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable<string>;
ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty;
}
private async void GetFormats(object sender, RoutedEventArgs args)
{
var formats = await Application.Current.Clipboard.GetFormatsAsync();
ClipboardContent.Text = string.Join(Environment.NewLine, formats);
}
private async void Clear(object sender, RoutedEventArgs args)
{
await Application.Current.Clipboard.ClearAsync();
}
}
}

8
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -11,7 +11,13 @@
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2">
<TextBlock Name="DragStateText" TextWrapping="Wrap">Drag Me</TextBlock>
<TextBlock Name="DragStateText" TextWrapping="Wrap">Drag Me (text)</TextBlock>
</Border>
<Border Name="DragMeFiles"
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2">
<TextBlock Name="DragStateFiles" TextWrapping="Wrap">Drag Me (files)</TextBlock>
</Border>
<Border Name="DragMeCustom"
Padding="16"

4
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -2,11 +2,8 @@
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ControlCatalog.Pages
{
@ -24,6 +21,7 @@ namespace ControlCatalog.Pages
$"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move);
SetupDnd("Files", d => d.Set(DataFormats.FileNames, new[] { Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }), DragDropEffects.Copy);
}
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)

2
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -111,7 +111,7 @@ namespace ControlCatalog.Pages
private void ScrollTo(int index)
{
System.Diagnostics.Debug.WriteLine("Scroll to " + index);
var layoutManager = ((Window)this.GetVisualRoot()).LayoutManager;
var layoutManager = ((TopLevel)VisualRoot).LayoutManager;
var element = _repeater.GetOrCreateElement(index);
layoutManager.ExecuteLayoutPass();
element.BringIntoView();

2
samples/ControlCatalog/Pages/ToggleSwitchPage.xaml

@ -11,7 +11,7 @@
</StackPanel>
</Border>
<TextBlock Text="headered ToggleSwitch" Classes="header"/>
<TextBlock Text="Headered ToggleSwitch" Classes="header"/>
<Border Classes="Thin">
<StackPanel>

4
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -25,7 +25,7 @@ namespace Avalonia.Android
_host.Focusable = true;
_host.FocusableInTouchMode = true;
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListner(_host));
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListener(_host));
}
public void Reset()
@ -83,6 +83,8 @@ namespace Avalonia.Android
if (options.Multiline)
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi;
});
//_inputElement.PointerReleased += RestoreSoftKeyboard;

9
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -10,7 +10,6 @@ using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.PlatformSupport;
using Avalonia.Skia;
namespace Avalonia
@ -20,9 +19,9 @@ namespace Avalonia
public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
{
var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
builder.UseWindowingSubsystem(() => AndroidPlatform.Initialize(builder.ApplicationType, options), "Android");
builder.UseSkia();
return builder;
return builder
.UseWindowingSubsystem(() => AndroidPlatform.Initialize(options), "Android")
.UseSkia();
}
}
}
@ -44,7 +43,7 @@ namespace Avalonia.Android
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500);
public static void Initialize(Type appType, AndroidPlatformOptions options)
public static void Initialize(AndroidPlatformOptions options)
{
Options = options;

14
src/Android/Avalonia.Android/AppBuilder.cs

@ -1,14 +0,0 @@
using Avalonia.Controls;
using Avalonia.PlatformSupport;
namespace Avalonia
{
public sealed class AppBuilder : AppBuilderBase<AppBuilder>
{
public AppBuilder() : base(new StandardRuntimePlatform(),
builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType()?.Assembly))
{
}
}
}

17
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -1,15 +1,18 @@
<Project Sdk="MSBuild.Sdk.Extras">
<Project Sdk="Xamarin.Legacy.Sdk">
<PropertyGroup>
<TargetFramework>monoandroid11.0</TargetFramework>
<TargetFrameworks>net6.0-android;monoandroid11.0</TargetFrameworks>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj">
<SetTargetFramework>TargetFramework=netstandard2.0</SetTargetFramework>
</ProjectReference>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" />
</Project>

36
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -1,35 +1,55 @@
using Android.App;
using Android.OS;
using Android.Views;
using Android.Content.PM;
using AndroidX.AppCompat.App;
using Android.Content.Res;
using AndroidX.Lifecycle;
namespace Avalonia.Android
{
public abstract class AvaloniaActivity : Activity
public abstract class AvaloniaActivity : AppCompatActivity
{
internal AvaloniaView View;
object _content;
internal AvaloniaViewModel _viewModel;
protected override void OnCreate(Bundle savedInstanceState)
{
View = new AvaloniaView(this);
if (_content != null)
View.Content = _content;
SetContentView(View);
_viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel;
if (_viewModel.Content != null)
{
View.Content = _viewModel.Content;
}
base.OnCreate(savedInstanceState);
}
public object Content
{
get
{
return _content;
return _viewModel.Content;
}
set
{
_content = value;
_viewModel.Content = value;
if (View != null)
View.Content = value;
}
}
public override void OnConfigurationChanged(Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
}
protected override void OnDestroy()
{
View.Content = null;
base.OnDestroy();
}
}
}

11
src/Android/Avalonia.Android/AvaloniaViewModel.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Android
{
internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel
{
public object Content { get; set; }
}
}

50
src/Android/Avalonia.Android/Resources/AboutResources.txt

@ -1,50 +0,0 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable-hdpi/
icon.png
drawable-ldpi/
icon.png
drawable-mdpi/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called
"Resource" that contains the tokens for each one of the resources included. For example,
for the above Resources layout, this is what the Resource class would expose:
public class Resource {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

6
src/Android/Avalonia.Android/Resources/Values/Strings.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="Hello">Hello World, Click Me!</string>
<string name="ApplicationName">$projectname$</string>
</resources>

4
src/Android/Avalonia.Android/SoftKeyboardListner.cs → src/Android/Avalonia.Android/SoftKeyboardListener.cs

@ -9,7 +9,7 @@ using Avalonia.Input;
namespace Avalonia.Android
{
class SoftKeyboardListner : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
class SoftKeyboardListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
private const int DefaultKeyboardHeightDP = 100;
private static readonly int EstimatedKeyboardDP = DefaultKeyboardHeightDP + (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop ? 48 : 0);
@ -17,7 +17,7 @@ namespace Avalonia.Android
private readonly View _host;
private bool _wasKeyboard;
public SoftKeyboardListner(View view)
public SoftKeyboardListener(View view)
{
_host = view;
}

19
src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt

@ -1,19 +0,0 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with you package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

172
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@ -1,153 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{FF69B927-C545-49AE-8E16-3D14D621AA12}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Avalonia.AndroidTestApplication</RootNamespace>
<AssemblyName>Avalonia.AndroidTestApplication</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ApplicationId>com.Avalonia.AndroidTestApplication</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a;x86</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<DevInstrumentationEnabled>True</DevInstrumentationEnabled>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<EnableProguard>False</EnableProguard>
<RunAOTCompilation>True</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
<!--<AndroidEnableProfiledAot>True</AndroidEnableProfiledAot>-->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>Full</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<EnableProguard>False</EnableProguard>
<DevInstrumentationEnabled>False</DevInstrumentationEnabled>
<DebugSymbols>False</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\Strings.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\Icon.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
<None Remove="Assets\AboutAssets.txt" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj">
<Project>{7b92af71-6287-4693-9dcb-bd5b6e927e23}</Project>
<Name>Avalonia.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Android\Avalonia.Android.csproj" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\..\build\Base.props" />
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\System.Memory.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" />
<Import Project="..\..\..\build\LegacyProject.targets" />
</Project>

2
src/Android/Avalonia.AndroidTestApplication/MainActivity.cs

@ -14,6 +14,8 @@ namespace Avalonia.AndroidTestApplication
[Activity(Label = "Main",
MainLauncher = true,
Icon = "@drawable/icon",
Theme = "@style/Theme.AppCompat.NoActionBar",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize,
LaunchMode = LaunchMode.SingleInstance/*,
ScreenOrientation = ScreenOrientation.Landscape*/)]
public class MainBaseActivity : AvaloniaActivity

6
src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="Avalonia.AndroidTestApplication" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:targetSdkVersion="30" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:label="Avalonia.AndroidTestApplication" android:icon="@drawable/Icon" android:hardwareAccelerated="true"></application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
</manifest>

30
src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs

@ -1,30 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.AndroidTestApplication")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Avalonia.AndroidTestApplication")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

79
src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs

@ -1,79 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Avalonia.AndroidTestApplication.Resource", IsApplication=true)]
namespace Avalonia.AndroidTestApplication
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F010000
public const int Icon = 2130771968;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class String
{
// aapt resource value: 0x7F020000
public const int ApplicationName = 2130837504;
// aapt resource value: 0x7F020001
public const int Hello = 2130837505;
static String()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private String()
{
}
}
}
}
#pragma warning restore 1591

11
src/Android/Avalonia.AndroidTestApplication/app.config

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices.WindowsRuntime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

6
src/Avalonia.Controls/Button.cs

@ -85,8 +85,8 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Flyout"/> property
/// </summary>
public static readonly StyledProperty<FlyoutBase> FlyoutProperty =
AvaloniaProperty.Register<Button, FlyoutBase>(nameof(Flyout));
public static readonly StyledProperty<FlyoutBase?> FlyoutProperty =
AvaloniaProperty.Register<Button, FlyoutBase?>(nameof(Flyout));
private ICommand? _command;
private bool _commandCanExecute = true;
@ -186,7 +186,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the Flyout that should be shown with this button.
/// </summary>
public FlyoutBase Flyout
public FlyoutBase? Flyout
{
get => GetValue(FlyoutProperty);
set => SetValue(FlyoutProperty, value);

3
src/Avalonia.Controls/Calendar/CalendarDatePicker.cs

@ -186,7 +186,8 @@ namespace Avalonia.Controls
nameof(SelectedDate),
o => o.SelectedDate,
(o, v) => o.SelectedDate = v,
enableDataValidation: true);
enableDataValidation: true,
defaultBindingMode:BindingMode.TwoWay);
public static readonly StyledProperty<CalendarDatePickerFormat> SelectedDateFormatProperty =
AvaloniaProperty.Register<CalendarDatePicker, CalendarDatePickerFormat>(

20
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -4,6 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Layout;
using System;
using System.Collections.Generic;
using System.Globalization;
@ -398,18 +399,27 @@ namespace Avalonia.Controls
private void OnFlyoutButtonClicked(object? sender, RoutedEventArgs e)
{
if (_presenter == null)
throw new InvalidOperationException("No DatePickerPresenter found");
throw new InvalidOperationException("No DatePickerPresenter found.");
if (_popup == null)
throw new InvalidOperationException("No Popup found.");
_presenter.Date = SelectedDate ?? DateTimeOffset.Now;
_popup!.IsOpen = true;
_popup.PlacementMode = PlacementMode.AnchorAndGravity;
_popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
_popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
_popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true;
// Overlay popup hosts won't get measured until the next layout pass, but we need the
// template to be applied to `_presenter` now. Detect this case and force a layout pass.
if (!_presenter.IsMeasureValid)
(VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
_popup!.Host!.ConfigurePosition(_popup.PlacementTarget!, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
_popup.VerticalOffset = deltaY + 5;
}
protected virtual void OnSelectedDateChanged(object? sender, DatePickerSelectedValueChangedEventArgs e)

5
src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs

@ -537,8 +537,11 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup()
{
if (_monthSelector is null)
return 0;
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector!.ItemHeight / 2);
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector.ItemHeight / 2);
}
}
}

2
src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs

@ -560,7 +560,7 @@ namespace Avalonia.Controls.Primitives
public bool BringIntoView(IControl target, Rect targetRect) { return false; }
public IControl? GetControlInDirection(NavigationDirection direction, IControl from) { return null; }
public IControl? GetControlInDirection(NavigationDirection direction, IControl? from) { return null; }
public void RaiseScrollInvalidated(EventArgs e)
{

23
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -3,6 +3,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Layout;
using System;
using System.Globalization;
@ -254,16 +255,28 @@ namespace Avalonia.Controls
private void OnFlyoutButtonClicked(object? sender, Interactivity.RoutedEventArgs e)
{
_presenter!.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
if (_presenter == null)
throw new InvalidOperationException("No DatePickerPresenter found.");
if (_popup == null)
throw new InvalidOperationException("No Popup found.");
_popup!.IsOpen = true;
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
_popup.PlacementMode = PlacementMode.AnchorAndGravity;
_popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
_popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
_popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true;
// Overlay popup hosts won't get measured until the next layout pass, but we need the
// template to be applied to `_presenter` now. Detect this case and force a layout pass.
if (!_presenter.IsMeasureValid)
(VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
_popup.Host!.ConfigurePosition(_popup.PlacementTarget!, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
_popup.VerticalOffset = deltaY + 5;
}
private void OnDismissPicker(object? sender, EventArgs e)

5
src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs

@ -256,8 +256,11 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup()
{
if (_hourSelector is null)
return 0;
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector!.ItemHeight / 2);
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector.ItemHeight / 2);
}
}
}

2
src/Avalonia.Controls/Generators/IItemContainerGenerator.cs

@ -97,6 +97,6 @@ namespace Avalonia.Controls.Generators
/// </summary>
/// <param name="container">The container.</param>
/// <returns>The index of the container, or -1 if not found.</returns>
int IndexFromContainer(IControl container);
int IndexFromContainer(IControl? container);
}
}

2
src/Avalonia.Controls/Generators/ItemContainerGenerator.cs

@ -158,7 +158,7 @@ namespace Avalonia.Controls.Generators
}
/// <inheritdoc/>
public int IndexFromContainer(IControl container)
public int IndexFromContainer(IControl? container)
{
foreach (var i in _containers)
{

2
src/Avalonia.Controls/Presenters/ItemVirtualizer.cs

@ -254,7 +254,7 @@ namespace Avalonia.Controls.Presenters
/// <param name="direction">The movement direction.</param>
/// <param name="from">The control from which movement begins.</param>
/// <returns>The control.</returns>
public virtual IControl? GetControlInDirection(NavigationDirection direction, IControl from)
public virtual IControl? GetControlInDirection(NavigationDirection direction, IControl? from)
{
return null;
}

2
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -226,7 +226,7 @@ namespace Avalonia.Controls.Presenters
InvalidateScroll();
}
public override IControl? GetControlInDirection(NavigationDirection direction, IControl from)
public override IControl? GetControlInDirection(NavigationDirection direction, IControl? from)
{
var generator = Owner.ItemContainerGenerator;
var panel = VirtualizingPanel;

2
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@ -117,7 +117,7 @@ namespace Avalonia.Controls.Presenters
}
/// <inheritdoc/>
IControl? ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl from)
IControl? ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl? from)
{
return Virtualizer?.GetControlInDirection(direction, from);
}

199
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reactive.Linq;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
@ -26,14 +24,14 @@ namespace Avalonia.Controls.Presenters
public static readonly StyledProperty<char> PasswordCharProperty =
AvaloniaProperty.Register<TextPresenter, char>(nameof(PasswordChar));
public static readonly StyledProperty<IBrush> SelectionBrushProperty =
AvaloniaProperty.Register<TextPresenter, IBrush>(nameof(SelectionBrushProperty));
public static readonly StyledProperty<IBrush?> SelectionBrushProperty =
AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(SelectionBrushProperty));
public static readonly StyledProperty<IBrush> SelectionForegroundBrushProperty =
AvaloniaProperty.Register<TextPresenter, IBrush>(nameof(SelectionForegroundBrushProperty));
public static readonly StyledProperty<IBrush?> SelectionForegroundBrushProperty =
AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(SelectionForegroundBrushProperty));
public static readonly StyledProperty<IBrush> CaretBrushProperty =
AvaloniaProperty.Register<TextPresenter, IBrush>(nameof(CaretBrushProperty));
public static readonly StyledProperty<IBrush?> CaretBrushProperty =
AvaloniaProperty.Register<TextPresenter, IBrush?>(nameof(CaretBrushProperty));
public static readonly DirectProperty<TextPresenter, int> SelectionStartProperty =
TextBox.SelectionStartProperty.AddOwner<TextPresenter>(
@ -48,8 +46,8 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Defines the <see cref="Text"/> property.
/// </summary>
public static readonly DirectProperty<TextPresenter, string> TextProperty =
AvaloniaProperty.RegisterDirect<TextPresenter, string>(
public static readonly DirectProperty<TextPresenter, string?> TextProperty =
AvaloniaProperty.RegisterDirect<TextPresenter, string?>(
nameof(Text),
o => o.Text,
(o, v) => o.Text = v);
@ -77,16 +75,14 @@ namespace Avalonia.Controls.Presenters
private int _selectionStart;
private int _selectionEnd;
private bool _caretBlink;
private string _text;
private string? _text;
private TextLayout? _textLayout;
private Size _constraint = Size.Infinity;
private Size _constraint;
private CharacterHit _lastCharacterHit;
private Rect _caretBounds;
private Point _navigationPosition;
private ScrollViewer? _scrollViewer;
static TextPresenter()
{
AffectsRender<TextPresenter>(CaretBrushProperty, SelectionBrushProperty);
@ -114,7 +110,7 @@ namespace Avalonia.Controls.Presenters
/// Gets or sets the text.
/// </summary>
[Content]
public string Text
public string? Text
{
get => _text;
set => SetAndRaise(TextProperty, ref _text, value);
@ -186,7 +182,7 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Gets the <see cref="TextLayout"/> used to render the text.
/// </summary>
public TextLayout? TextLayout
public TextLayout TextLayout
{
get
{
@ -230,19 +226,19 @@ namespace Avalonia.Controls.Presenters
set => SetValue(RevealPasswordProperty, value);
}
public IBrush SelectionBrush
public IBrush? SelectionBrush
{
get => GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
public IBrush SelectionForegroundBrush
public IBrush? SelectionForegroundBrush
{
get => GetValue(SelectionForegroundBrushProperty);
set => SetValue(SelectionForegroundBrushProperty, value);
}
public IBrush CaretBrush
public IBrush? CaretBrush
{
get => GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
@ -284,20 +280,14 @@ namespace Avalonia.Controls.Presenters
/// <param name="typeface"></param>
/// <param name="textStyleOverrides"></param>
/// <returns>A <see cref="TextLayout"/> object.</returns>
private TextLayout? CreateTextLayoutInternal(Size constraint, string text, Typeface typeface,
private TextLayout CreateTextLayoutInternal(Size constraint, string? text, Typeface typeface,
IReadOnlyList<ValueSpan<TextRunProperties>>? textStyleOverrides)
{
var foreground = Foreground;
if (foreground == null)
{
return null;
}
var maxWidth = MathUtilities.IsZero(constraint.Width) ? double.PositiveInfinity : constraint.Width;
var maxHeight = MathUtilities.IsZero(constraint.Height) ? double.PositiveInfinity : constraint.Height;
var textLayout = new TextLayout(text ?? string.Empty, typeface, FontSize, foreground, TextAlignment,
var textLayout = new TextLayout(text, typeface, FontSize, foreground, TextAlignment,
TextWrapping, maxWidth: maxWidth, maxHeight: maxHeight, textStyleOverrides: textStyleOverrides,
flowDirection: FlowDirection);
@ -317,11 +307,6 @@ namespace Avalonia.Controls.Presenters
context.FillRectangle(background, new Rect(Bounds.Size));
}
if (TextLayout == null)
{
return;
}
var top = 0d;
var left = 0.0;
@ -346,17 +331,11 @@ namespace Avalonia.Controls.Presenters
public override void Render(DrawingContext context)
{
if (double.IsPositiveInfinity (_constraint.Width))
{
_constraint = _scrollViewer?.Viewport ?? Size.Infinity;
InvalidateTextLayout();
}
var selectionStart = SelectionStart;
var selectionEnd = SelectionEnd;
var selectionBrush = SelectionBrush;
if (selectionStart != selectionEnd && TextLayout != null)
if (selectionStart != selectionEnd && selectionBrush != null)
{
var start = Math.Min(selectionStart, selectionEnd);
var length = Math.Max(selectionStart, selectionEnd) - start;
@ -365,7 +344,7 @@ namespace Avalonia.Controls.Presenters
foreach (var rect in rects)
{
context.FillRectangle(SelectionBrush, rect);
context.FillRectangle(selectionBrush, rect);
}
}
@ -431,38 +410,40 @@ namespace Avalonia.Controls.Presenters
internal void CaretChanged()
{
if (this.GetVisualParent() != null)
if (this.GetVisualParent() == null)
{
if (_caretTimer.IsEnabled)
{
_caretBlink = true;
_caretTimer.Stop();
_caretTimer.Start();
InvalidateVisual();
}
else
{
_caretTimer.Start();
InvalidateVisual();
_caretTimer.Stop();
}
return;
}
if (IsMeasureValid)
{
this.BringIntoView(_caretBounds);
}
else
{
// The measure is currently invalid so there's no point trying to bring the
// current char into view until a measure has been carried out as the scroll
// viewer extents may not be up-to-date.
Dispatcher.UIThread.Post(
() =>
{
this.BringIntoView(_caretBounds);
},
DispatcherPriority.Render);
}
if (_caretTimer.IsEnabled)
{
_caretBlink = true;
_caretTimer.Stop();
_caretTimer.Start();
InvalidateVisual();
}
else
{
_caretTimer.Start();
InvalidateVisual();
_caretTimer.Stop();
}
if (IsMeasureValid)
{
this.BringIntoView(_caretBounds);
}
else
{
// The measure is currently invalid so there's no point trying to bring the
// current char into view until a measure has been carried out as the scroll
// viewer extents may not be up-to-date.
Dispatcher.UIThread.Post(
() =>
{
this.BringIntoView(_caretBounds);
},
DispatcherPriority.Render);
}
}
@ -470,9 +451,9 @@ namespace Avalonia.Controls.Presenters
/// Creates the <see cref="TextLayout"/> used to render the text.
/// </summary>
/// <returns>A <see cref="TextLayout"/> object.</returns>
protected virtual TextLayout? CreateTextLayout()
protected virtual TextLayout CreateTextLayout()
{
TextLayout? result;
TextLayout result;
var text = Text;
@ -517,14 +498,25 @@ namespace Avalonia.Controls.Presenters
protected override Size MeasureOverride(Size availableSize)
{
if (!double.IsInfinity(availableSize.Width) && availableSize != _constraint)
_textLayout = null;
_constraint = availableSize;
return TextLayout.Size;
}
protected override Size ArrangeOverride(Size finalSize)
{
if (!double.IsInfinity(_constraint.Width))
{
_constraint = availableSize;
InvalidateTextLayout();
return base.ArrangeOverride(finalSize);
}
_constraint = finalSize;
_textLayout = null;
return TextLayout?.Size ?? default;
return base.ArrangeOverride(finalSize);
}
private int CoerceCaretIndex(int value)
@ -537,16 +529,12 @@ namespace Avalonia.Controls.Presenters
private void CaretTimerTick(object? sender, EventArgs e)
{
_caretBlink = !_caretBlink;
InvalidateVisual();
}
public void MoveCaretToTextPosition(int textPosition, bool trailingEdge = false)
{
if (TextLayout == null)
{
return;
}
var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(textPosition, trailingEdge);
var textLine = TextLayout.TextLines[lineIndex];
@ -569,29 +557,23 @@ namespace Avalonia.Controls.Presenters
}
_navigationPosition = _caretBounds.Position;
CaretChanged();
}
public void MoveCaretToPoint(Point point)
{
if (TextLayout == null)
{
return;
}
var hit = TextLayout.HitTestPoint(point);
UpdateCaret(hit.CharacterHit);
_navigationPosition = _caretBounds.Position;
CaretChanged();
}
public void MoveCaretVertical(LogicalDirection direction = LogicalDirection.Forward)
{
if (TextLayout == null)
{
return;
}
var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(CaretIndex, _lastCharacterHit.TrailingLength > 0);
if (lineIndex < 0)
@ -629,14 +611,23 @@ namespace Avalonia.Controls.Presenters
MoveCaretToPoint(new Point(currentX, currentY));
_navigationPosition = navigationPosition.WithY(_caretBounds.Y);
CaretChanged();
}
public void MoveCaretHorizontal(LogicalDirection direction = LogicalDirection.Forward)
{
if (TextLayout == null)
if (Text is null)
{
return;
}
if (FlowDirection == FlowDirection.RightToLeft)
{
direction = direction == LogicalDirection.Forward ?
LogicalDirection.Backward :
LogicalDirection.Forward;
}
var characterHit = _lastCharacterHit;
var caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
@ -709,15 +700,12 @@ namespace Avalonia.Controls.Presenters
UpdateCaret(characterHit);
_navigationPosition = _caretBounds.Position;
CaretChanged();
}
private void UpdateCaret(CharacterHit characterHit)
{
if (TextLayout == null)
{
return;
}
_lastCharacterHit = characterHit;
var caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
@ -743,9 +731,7 @@ namespace Avalonia.Controls.Presenters
CaretBoundsChanged?.Invoke(this, EventArgs.Empty);
}
CaretChanged();
SetAndRaise(CaretIndexProperty, ref _caretIndex, caretIndex);
}
@ -754,19 +740,10 @@ namespace Avalonia.Controls.Presenters
return _caretBounds;
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_scrollViewer = this.FindAncestorOfType<ScrollViewer>();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
_scrollViewer = null;
_caretTimer.Stop();
_caretTimer.Tick -= CaretTimerTick;

2
src/Avalonia.Controls/Primitives/ILogicalScrollable.cs

@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives
/// <param name="direction">The movement direction.</param>
/// <param name="from">The control from which movement begins.</param>
/// <returns>The control.</returns>
IControl? GetControlInDirection(NavigationDirection direction, IControl from);
IControl? GetControlInDirection(NavigationDirection direction, IControl? from);
/// <summary>
/// Raises the <see cref="ScrollInvalidated"/> event.

20
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -531,11 +531,23 @@ namespace Avalonia.Controls.Primitives
_textSearchTerm += e.Text;
bool match(ItemContainerInfo info) =>
info.ContainerControl is IContentControl control &&
control.Content?.ToString()?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true;
bool Match(ItemContainerInfo info)
{
if (info.ContainerControl.IsSet(TextSearch.TextProperty))
{
var searchText = info.ContainerControl.GetValue(TextSearch.TextProperty);
var info = ItemContainerGenerator?.Containers.FirstOrDefault(match);
if (searchText?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
}
return info.ContainerControl is IContentControl control &&
control.Content?.ToString()?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true;
}
var info = ItemContainerGenerator?.Containers.FirstOrDefault(Match);
if (info != null)
{

37
src/Avalonia.Controls/Primitives/TextSearch.cs

@ -0,0 +1,37 @@
using Avalonia.Interactivity;
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// Allows to customize text searching in <see cref="SelectingItemsControl"/>.
/// </summary>
public static class TextSearch
{
/// <summary>
/// Defines the Text attached property.
/// This text will be considered during text search in <see cref="SelectingItemsControl"/> (such as <see cref="ComboBox"/>)
/// </summary>
public static readonly AttachedProperty<string> TextProperty
= AvaloniaProperty.RegisterAttached<Interactive, string>("Text", typeof(TextSearch));
/// <summary>
/// Sets the <see cref="TextProperty"/> for a control.
/// </summary>
/// <param name="control">The control</param>
/// <param name="text">The search text to set</param>
public static void SetText(Control control, string text)
{
control.SetValue(TextProperty, text);
}
/// <summary>
/// Gets the <see cref="TextProperty"/> of a control.
/// </summary>
/// <param name="control">The control</param>
/// <returns>The property value</returns>
public static string GetText(Control control)
{
return control.GetValue(TextProperty);
}
}
}

485
src/Avalonia.Controls/SplitButton/SplitButton.cs

@ -0,0 +1,485 @@
using System;
using System.Windows.Input;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
namespace Avalonia.Controls
{
/// <summary>
/// A button with primary and secondary parts that can each be pressed separately.
/// The primary part behaves like a <see cref="Button"/> and the secondary part opens a flyout.
/// </summary>
[PseudoClasses(pcFlyoutOpen, pcPressed)]
public class SplitButton : ContentControl, ICommandSource
{
protected const string pcChecked = ":checked";
protected const string pcPressed = ":pressed";
protected const string pcFlyoutOpen = ":flyout-open";
/// <summary>
/// Raised when the user presses the primary part of the <see cref="SplitButton"/>.
/// </summary>
public event EventHandler<RoutedEventArgs> Click
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
/// <summary>
/// Defines the <see cref="Click"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
RoutedEvent.Register<SplitButton, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="Command"/> property.
/// </summary>
public static readonly DirectProperty<SplitButton, ICommand?> CommandProperty =
Button.CommandProperty.AddOwner<SplitButton>(
splitButton => splitButton.Command,
(splitButton, command) => splitButton.Command = command);
/// <summary>
/// Defines the <see cref="CommandParameter"/> property.
/// </summary>
public static readonly StyledProperty<object?> CommandParameterProperty =
Button.CommandParameterProperty.AddOwner<SplitButton>();
/// <summary>
/// Defines the <see cref="Flyout"/> property
/// </summary>
public static readonly StyledProperty<FlyoutBase?> FlyoutProperty =
Button.FlyoutProperty.AddOwner<SplitButton>();
private ICommand? _Command;
private Button? _primaryButton = null;
private Button? _secondaryButton = null;
private bool _commandCanExecute = true;
private bool _isAttachedToLogicalTree = false;
private bool _isFlyoutOpen = false;
private bool _isKeyboardPressed = false;
private IDisposable? _flyoutPropertyChangedDisposable;
////////////////////////////////////////////////////////////////////////
// Constructor / Destructors
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Initializes a new instance of the <see cref="SplitButton"/> class.
/// </summary>
public SplitButton()
{
}
////////////////////////////////////////////////////////////////////////
// Properties
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets or sets the <see cref="ICommand"/> invoked when the primary part is pressed.
/// </summary>
public ICommand? Command
{
get => _Command;
set => SetAndRaise(CommandProperty, ref _Command, value);
}
/// <summary>
/// Gets or sets a parameter to be passed to the <see cref="Command"/>.
/// </summary>
public object? CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
/// <summary>
/// Gets or sets the <see cref="FlyoutBase"/> that is shown when the secondary part is pressed.
/// </summary>
public FlyoutBase? Flyout
{
get => GetValue(FlyoutProperty);
set => SetValue(FlyoutProperty, value);
}
/// <summary>
/// Gets a value indicating whether the button is currently checked.
/// </summary>
/// <remarks>
/// This property exists only for the derived <see cref="ToggleSplitButton"/> and is
/// unused (set to false) within <see cref="SplitButton"/>. Doing this allows the
/// two controls to share a default style.
/// </remarks>
internal virtual bool InternalIsChecked => false;
/// <inheritdoc/>
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute;
////////////////////////////////////////////////////////////////////////
// Methods
////////////////////////////////////////////////////////////////////////
/// <inheritdoc/>
void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e);
/// <inheritdoc cref="ICommandSource.CanExecuteChanged"/>
private void CanExecuteChanged(object? sender, EventArgs e)
{
var canExecute = Command == null || Command.CanExecute(CommandParameter);
if (canExecute != _commandCanExecute)
{
_commandCanExecute = canExecute;
UpdateIsEffectivelyEnabled();
}
}
/// <summary>
/// Updates the visual state of the control by applying latest PseudoClasses.
/// </summary>
protected void UpdatePseudoClasses()
{
PseudoClasses.Set(pcFlyoutOpen, _isFlyoutOpen);
PseudoClasses.Set(pcPressed, _isKeyboardPressed);
PseudoClasses.Set(pcChecked, InternalIsChecked);
}
/// <summary>
/// Opens the secondary button's flyout.
/// </summary>
protected void OpenFlyout()
{
if (Flyout != null)
{
Flyout.ShowAt(this);
}
}
/// <summary>
/// Closes the secondary button's flyout.
/// </summary>
protected void CloseFlyout()
{
if (Flyout != null)
{
Flyout.Hide();
}
}
/// <summary>
/// Registers all flyout events.
/// </summary>
/// <param name="flyout">The flyout to connect events to.</param>
private void RegisterFlyoutEvents(FlyoutBase? flyout)
{
if (flyout != null)
{
flyout.Opened += Flyout_Opened;
flyout.Closed += Flyout_Closed;
_flyoutPropertyChangedDisposable = flyout.GetPropertyChangedObservable(FlyoutBase.PlacementProperty).Subscribe(Flyout_PlacementPropertyChanged);
}
}
/// <summary>
/// Explicitly unregisters all flyout events.
/// </summary>
/// <param name="flyout">The flyout to disconnect events from.</param>
private void UnregisterFlyoutEvents(FlyoutBase? flyout)
{
if (flyout != null)
{
flyout.Opened -= Flyout_Opened;
flyout.Closed -= Flyout_Closed;
_flyoutPropertyChangedDisposable?.Dispose();
_flyoutPropertyChangedDisposable = null;
}
}
/// <summary>
/// Explicitly unregisters all events related to the two buttons in OnApplyTemplate().
/// </summary>
private void UnregisterEvents()
{
if (_primaryButton != null)
{
_primaryButton.Click -= PrimaryButton_Click;
}
if (_secondaryButton != null)
{
_secondaryButton.Click -= SecondaryButton_Click;
}
}
////////////////////////////////////////////////////////////////////////
// OnEvent Overridable Methods
////////////////////////////////////////////////////////////////////////
/// <inheritdoc/>
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
UnregisterEvents();
UnregisterFlyoutEvents(Flyout);
_primaryButton = e.NameScope.Find<Button>("PART_PrimaryButton");
_secondaryButton = e.NameScope.Find<Button>("PART_SecondaryButton");
if (_primaryButton != null)
{
_primaryButton.Click += PrimaryButton_Click;
}
if (_secondaryButton != null)
{
_secondaryButton.Click += SecondaryButton_Click;
}
RegisterFlyoutEvents(Flyout);
UpdatePseudoClasses();
}
/// <inheritdoc/>
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
if (Command != null)
{
Command.CanExecuteChanged += CanExecuteChanged;
CanExecuteChanged(this, EventArgs.Empty);
}
_isAttachedToLogicalTree = true;
}
/// <inheritdoc/>
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
if (Command != null)
{
Command.CanExecuteChanged -= CanExecuteChanged;
}
_isAttachedToLogicalTree = false;
}
/// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> e)
{
if (e.Property == CommandProperty)
{
if (_isAttachedToLogicalTree)
{
// Must unregister events here while a reference to the old command still exists
if (e.OldValue.GetValueOrDefault() is ICommand oldCommand)
{
oldCommand.CanExecuteChanged -= CanExecuteChanged;
}
if (e.NewValue.GetValueOrDefault() is ICommand newCommand)
{
newCommand.CanExecuteChanged += CanExecuteChanged;
}
}
CanExecuteChanged(this, EventArgs.Empty);
}
else if (e.Property == CommandParameterProperty)
{
CanExecuteChanged(this, EventArgs.Empty);
}
else if (e.Property == FlyoutProperty)
{
var oldFlyout = e.OldValue.GetValueOrDefault() as FlyoutBase;
var newFlyout = e.NewValue.GetValueOrDefault() as FlyoutBase;
// If flyout is changed while one is already open, make sure we
// close the old one first
// This is the same behavior as Button
if (oldFlyout != null &&
oldFlyout.IsOpen)
{
oldFlyout.Hide();
}
// Must unregister events here while a reference to the old flyout still exists
if (oldFlyout != null)
{
UnregisterFlyoutEvents(oldFlyout);
}
if (newFlyout != null)
{
RegisterFlyoutEvents(newFlyout);
}
UpdatePseudoClasses();
}
base.OnPropertyChanged(e);
}
/// <inheritdoc/>
protected override void OnKeyDown(KeyEventArgs e)
{
var key = e.Key;
if (key == Key.Space || key == Key.Enter) // Key.GamepadA is not currently supported
{
_isKeyboardPressed = true;
UpdatePseudoClasses();
}
base.OnKeyDown(e);
}
/// <inheritdoc/>
protected override void OnKeyUp(KeyEventArgs e)
{
var key = e.Key;
if (key == Key.Space || key == Key.Enter) // Key.GamepadA is not currently supported
{
_isKeyboardPressed = false;
UpdatePseudoClasses();
// Consider this a click on the primary button
if (IsEffectivelyEnabled)
{
OnClickPrimary(null);
e.Handled = true;
}
}
else if (key == Key.Down && e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) && IsEffectivelyEnabled)
{
OpenFlyout();
e.Handled = true;
}
else if (key == Key.F4 && IsEffectivelyEnabled)
{
OpenFlyout();
e.Handled = true;
}
else if (e.Key == Key.Escape && _isFlyoutOpen)
{
// If Flyout doesn't have focusable content, close the flyout here
// This is the same behavior as Button
CloseFlyout();
e.Handled = true;
}
base.OnKeyUp(e);
}
/// <summary>
/// Invokes the <see cref="Click"/> event when the primary button part is clicked.
/// </summary>
/// <param name="e">The event args from the internal Click event.</param>
protected virtual void OnClickPrimary(RoutedEventArgs? e)
{
// Note: It is not currently required to check enabled status; however, this is a failsafe
if (IsEffectivelyEnabled)
{
var eventArgs = new RoutedEventArgs(ClickEvent);
RaiseEvent(eventArgs);
if (!eventArgs.Handled && Command?.CanExecute(CommandParameter) == true)
{
Command.Execute(CommandParameter);
eventArgs.Handled = true;
}
}
}
/// <summary>
/// Invoked when the secondary button part is clicked.
/// </summary>
/// <param name="e">The event args from the internal Click event.</param>
protected virtual void OnClickSecondary(RoutedEventArgs? e)
{
// Note: It is not currently required to check enabled status; however, this is a failsafe
if (IsEffectivelyEnabled)
{
OpenFlyout();
}
}
////////////////////////////////////////////////////////////////////////
// Event Handling
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Event handler for when the internal primary button part is pressed.
/// </summary>
private void PrimaryButton_Click(object? sender, RoutedEventArgs e)
{
// Handle internal button click, so it won't bubble outside together with SplitButton.ClickEvent.
e.Handled = true;
OnClickPrimary(e);
}
/// <summary>
/// Event handler for when the internal secondary button part is pressed.
/// </summary>
private void SecondaryButton_Click(object? sender, RoutedEventArgs e)
{
// Handle internal button click, so it won't bubble outside.
e.Handled = true;
OnClickSecondary(e);
}
/// <summary>
/// Called when the <see cref="FlyoutBase.Placement"/> property changes.
/// </summary>
private void Flyout_PlacementPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
UpdatePseudoClasses();
}
/// <summary>
/// Event handler for when the split button's flyout is opened.
/// </summary>
private void Flyout_Opened(object? sender, EventArgs e)
{
var flyout = sender as FlyoutBase;
// It is possible to share flyouts among multiple controls including SplitButton.
// This can cause a problem here since all controls that share a flyout receive
// the same Opened/Closed events at the same time.
// For SplitButton that means they all would be updating their pseudoclasses accordingly.
// In other words, all SplitButtons with a shared Flyout would have the backgrounds changed together.
// To fix this, only continue here if the Flyout target matches this SplitButton instance.
if (object.ReferenceEquals(flyout?.Target, this))
{
_isFlyoutOpen = true;
UpdatePseudoClasses();
}
}
/// <summary>
/// Event handler for when the split button's flyout is closed.
/// </summary>
private void Flyout_Closed(object? sender, EventArgs e)
{
var flyout = sender as FlyoutBase;
// See comments in Flyout_Opened
if (object.ReferenceEquals(flyout?.Target, this))
{
_isFlyoutOpen = false;
UpdatePseudoClasses();
}
}
}
}

125
src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs

@ -0,0 +1,125 @@
using System;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Styling;
namespace Avalonia.Controls
{
/// <summary>
/// A button with primary and secondary parts that can each be pressed separately.
/// The primary part behaves like a <see cref="ToggleButton"/> with two states and
/// the secondary part opens a flyout.
/// </summary>
[PseudoClasses(pcChecked)]
public class ToggleSplitButton : SplitButton, IStyleable
{
/// <summary>
/// Raised when the <see cref="IsChecked"/> property value changes.
/// </summary>
public event EventHandler<RoutedEventArgs> IsCheckedChanged
{
add => AddHandler(IsCheckedChangedEvent, value);
remove => RemoveHandler(IsCheckedChangedEvent, value);
}
/// <summary>
/// Defines the <see cref="IsCheckedChanged"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> IsCheckedChangedEvent =
RoutedEvent.Register<ToggleSplitButton, RoutedEventArgs>(
nameof(IsCheckedChanged),
RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="IsChecked"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsCheckedProperty =
AvaloniaProperty.Register<ToggleSplitButton, bool>(
nameof(IsChecked));
////////////////////////////////////////////////////////////////////////
// Constructor / Destructors
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Initializes a new instance of the <see cref="ToggleSplitButton"/> class.
/// </summary>
public ToggleSplitButton()
{
}
////////////////////////////////////////////////////////////////////////
// Properties
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets or sets a value indicating whether the <see cref="ToggleSplitButton"/> is checked.
/// </summary>
public bool IsChecked
{
get => GetValue(IsCheckedProperty);
set => SetValue(IsCheckedProperty, value);
}
/// <inheritdoc/>
internal override bool InternalIsChecked => IsChecked;
/// <inheritdoc/>
/// <remarks>
/// Both <see cref="ToggleSplitButton"/> and <see cref="SplitButton"/> share
/// the same exact default style.
/// </remarks>
Type IStyleable.StyleKey => typeof(SplitButton);
////////////////////////////////////////////////////////////////////////
// Methods
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Toggles the <see cref="IsChecked"/> property between true and false.
/// </summary>
protected void Toggle()
{
IsChecked = !IsChecked;
}
////////////////////////////////////////////////////////////////////////
// OnEvent Overridable Methods
////////////////////////////////////////////////////////////////////////
/// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> e)
{
if (e.Property == IsCheckedProperty)
{
OnIsCheckedChanged();
}
}
/// <summary>
/// Invokes the <see cref="IsCheckedChanged"/> event when the <see cref="IsChecked"/>
/// property changes.
/// </summary>
protected virtual void OnIsCheckedChanged()
{
// IsLoaded check
if (Parent is not null)
{
var eventArgs = new RoutedEventArgs(IsCheckedChangedEvent);
RaiseEvent(eventArgs);
}
UpdatePseudoClasses();
}
/// <inheritdoc/>
protected override void OnClickPrimary(RoutedEventArgs? e)
{
Toggle();
base.OnClickPrimary(e);
}
}
}

7
src/Avalonia.Controls/TextBlock.cs

@ -492,13 +492,6 @@ namespace Avalonia.Controls
return measuredSize.Inflate(padding);
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
InvalidateTextLayout();
}
private static bool IsValidMaxLines(int maxLines) => maxLines >= 0;
private static bool IsValidLineHeight(double lineHeight) => double.IsNaN(lineHeight) || lineHeight > 0;

25
src/Avalonia.Controls/TextBox.cs

@ -540,14 +540,19 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter");
_imClient.SetPresenter(_presenter, this);
if (IsFocused)
{
_presenter?.ShowCaret();
}
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_imClient.SetPresenter(_presenter, this);
if (IsFocused)
{
_presenter?.ShowCaret();
@ -607,6 +612,8 @@ namespace Avalonia.Controls
}
UpdateCommandStates();
_imClient.SetPresenter(_presenter, this);
_presenter?.ShowCaret();
}
@ -625,6 +632,8 @@ namespace Avalonia.Controls
UpdateCommandStates();
_presenter?.HideCaret();
_imClient.SetPresenter(null, null);
}
protected override void OnTextInput(TextInputEventArgs e)
@ -930,6 +939,10 @@ namespace Avalonia.Controls
{
SelectionEnd = _presenter.CaretIndex;
}
else
{
SelectionStart = SelectionEnd = _presenter.CaretIndex;
}
break;
}
@ -948,6 +961,10 @@ namespace Avalonia.Controls
{
SelectionEnd = _presenter.CaretIndex;
}
else
{
SelectionStart = SelectionEnd = _presenter.CaretIndex;
}
break;
}
@ -1273,7 +1290,7 @@ namespace Avalonia.Controls
{
caretIndex = 0;
}
else if (_presenter.TextLayout is not null)
else
{
var lines = _presenter.TextLayout.TextLines;
var pos = 0;
@ -1308,7 +1325,7 @@ namespace Avalonia.Controls
{
caretIndex = text.Length;
}
else if (_presenter.TextLayout is not null)
else
{
var lines = _presenter.TextLayout.TextLines;
var pos = 0;

96
src/Avalonia.Controls/TransitioningContentControl.cs

@ -0,0 +1,96 @@
using System;
using System.Threading;
using Avalonia.Animation;
using Avalonia.Controls.Templates;
using Avalonia.Threading;
namespace Avalonia.Controls;
/// <summary>
/// Displays <see cref="ContentControl.Content"/> according to a <see cref="FuncDataTemplate"/>.
/// Uses <see cref="PageTransition"/> to move between the old and new content values.
/// </summary>
public class TransitioningContentControl : ContentControl
{
private CancellationTokenSource? _lastTransitionCts;
private object? _currentContent;
/// <summary>
/// Defines the <see cref="PageTransition"/> property.
/// </summary>
public static readonly StyledProperty<IPageTransition?> PageTransitionProperty =
AvaloniaProperty.Register<TransitioningContentControl, IPageTransition?>(nameof(PageTransition),
new CrossFade(TimeSpan.FromSeconds(0.125)));
/// <summary>
/// Defines the <see cref="CurrentContent"/> property.
/// </summary>
public static readonly DirectProperty<TransitioningContentControl, object?> CurrentContentProperty =
AvaloniaProperty.RegisterDirect<TransitioningContentControl, object?>(nameof(CurrentContent),
o => o.CurrentContent);
/// <summary>
/// Gets or sets the animation played when content appears and disappears.
/// </summary>
public IPageTransition? PageTransition
{
get => GetValue(PageTransitionProperty);
set => SetValue(PageTransitionProperty, value);
}
/// <summary>
/// Gets the content currently displayed on the screen.
/// </summary>
public object? CurrentContent
{
get => _currentContent;
private set => SetAndRaise(CurrentContentProperty, ref _currentContent, value);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content));
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
_lastTransitionCts?.Cancel();
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == ContentProperty)
{
Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content));
}
}
/// <summary>
/// Updates the content with transitions.
/// </summary>
/// <param name="content">New content to set.</param>
private async void UpdateContentWithTransition(object? content)
{
if (VisualRoot is null)
{
return;
}
_lastTransitionCts?.Cancel();
_lastTransitionCts = new CancellationTokenSource();
if (PageTransition != null)
await PageTransition.Start(this, null, true, _lastTransitionCts.Token);
CurrentContent = content;
if (PageTransition != null)
await PageTransition.Start(null, this, true, _lastTransitionCts.Token);
}
}

2
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -133,7 +133,7 @@ namespace Avalonia.Controls
if (logicalScrollable?.IsLogicalScrollEnabled == true)
{
return logicalScrollable.GetControlInDirection(direction, from!);
return logicalScrollable.GetControlInDirection(direction, from);
}
else
{

6
src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs

@ -84,7 +84,8 @@ namespace Avalonia.Diagnostics.ViewModels
c.GetObservable(Control.ContextMenuProperty),
c.GetObservable(FlyoutBase.AttachedFlyoutProperty),
c.GetObservable(ToolTipDiagnostics.ToolTipProperty),
(ContextFlyout, ContextMenu, AttachedFlyout, ToolTip) =>
c.GetObservable(Button.FlyoutProperty),
(ContextFlyout, ContextMenu, AttachedFlyout, ToolTip, ButtonFlyout) =>
{
if (ContextMenu != null)
//Note: ContextMenus are special since all the items are added as visual children.
@ -100,6 +101,9 @@ namespace Avalonia.Diagnostics.ViewModels
if (ToolTip != null)
return GetPopupHostObservable(ToolTip, "ToolTip");
if (ButtonFlyout != null)
return GetPopupHostObservable(ButtonFlyout, "Flyout");
return Observable.Return<PopupRoot?>(null);
})
.Switch(),

1
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs

@ -147,6 +147,7 @@ namespace Avalonia.Diagnostics.Views
ProcessProperty(control, ContextMenuProperty);
ProcessProperty(control, FlyoutBase.AttachedFlyoutProperty);
ProcessProperty(control, ToolTipDiagnostics.ToolTipProperty);
ProcessProperty(control, Button.FlyoutProperty);
}
return popupRoots;

7
src/Avalonia.Input/ICommandSource.cs

@ -20,12 +20,11 @@ namespace Avalonia.Input
/// </summary>
object? CommandParameter { get; }
/// <summary>
/// Bor the behavior CanExecuteChanged
/// Called for the CanExecuteChanged event when changes are detected.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
void CanExecuteChanged(object sender, System.EventArgs e);
/// <summary>

10
src/Avalonia.Input/MouseDevice.cs

@ -5,6 +5,7 @@ using System.Reactive.Linq;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.Platform;
using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Input
@ -273,7 +274,7 @@ namespace Avalonia.Input
}
private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties,
KeyModifiers inputModifiers, IReadOnlyList<Point>? intermediatePoints)
KeyModifiers inputModifiers, Lazy<IReadOnlyList<RawPointerPoint>?>? intermediatePoints)
{
device = device ?? throw new ArgumentNullException(nameof(device));
root = root ?? throw new ArgumentNullException(nameof(root));
@ -334,6 +335,13 @@ namespace Avalonia.Input
var hit = HitTest(root, p);
var source = GetSource(hit);
// KeyModifiers.Shift should scroll in horizontal direction. This does not work on every platform.
// If Shift-Key is pressed and X is close to 0 we swap the Vector.
if (inputModifiers == KeyModifiers.Shift && MathUtilities.IsZero(delta.X))
{
delta = new Vector(delta.Y, delta.X);
}
if (source is not null)
{
var e = new PointerWheelEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, delta);

15
src/Avalonia.Input/PointerEventArgs.cs

@ -11,7 +11,7 @@ namespace Avalonia.Input
private readonly IVisual? _rootVisual;
private readonly Point _rootVisualPosition;
private readonly PointerPointProperties _properties;
private readonly IReadOnlyList<Point>? _previousPoints;
private Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
public PointerEventArgs(RoutedEvent routedEvent,
IInteractive? source,
@ -38,7 +38,7 @@ namespace Avalonia.Input
ulong timestamp,
PointerPointProperties properties,
KeyModifiers modifiers,
IReadOnlyList<Point>? previousPoints)
Lazy<IReadOnlyList<RawPointerPoint>?>? previousPoints)
: this(routedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers)
{
_previousPoints = previousPoints;
@ -121,13 +121,14 @@ namespace Avalonia.Input
/// <returns></returns>
public IReadOnlyList<PointerPoint> GetIntermediatePoints(IVisual? relativeTo)
{
if (_previousPoints == null || _previousPoints.Count == 0)
var previousPoints = _previousPoints?.Value;
if (previousPoints == null || previousPoints.Count == 0)
return new[] { GetCurrentPoint(relativeTo) };
var points = new PointerPoint[_previousPoints.Count + 1];
for (var c = 0; c < _previousPoints.Count; c++)
var points = new PointerPoint[previousPoints.Count + 1];
for (var c = 0; c < previousPoints.Count; c++)
{
var pt = _previousPoints[c];
points[c] = new PointerPoint(Pointer, GetPosition(pt, relativeTo), _properties);
var pt = previousPoints[c];
points[c] = new PointerPoint(Pointer, GetPosition(pt.Position, relativeTo), _properties);
}
points[points.Length - 1] = GetCurrentPoint(relativeTo);

58
src/Avalonia.Input/Raw/RawPointerEventArgs.cs

@ -33,6 +33,8 @@ namespace Avalonia.Input.Raw
/// </summary>
public class RawPointerEventArgs : RawInputEventArgs
{
private RawPointerPoint _point;
/// <summary>
/// Initializes a new instance of the <see cref="RawPointerEventArgs"/> class.
/// </summary>
@ -58,11 +60,50 @@ namespace Avalonia.Input.Raw
Type = type;
InputModifiers = inputModifiers;
}
/// <summary>
/// Initializes a new instance of the <see cref="RawPointerEventArgs"/> class.
/// </summary>
/// <param name="device">The associated device.</param>
/// <param name="timestamp">The event timestamp.</param>
/// <param name="root">The root from which the event originates.</param>
/// <param name="type">The type of the event.</param>
/// <param name="point">The point properties and position, in client DIPs.</param>
/// <param name="inputModifiers">The input modifiers.</param>
public RawPointerEventArgs(
IInputDevice device,
ulong timestamp,
IInputRoot root,
RawPointerEventType type,
RawPointerPoint point,
RawInputModifiers inputModifiers)
: base(device, timestamp, root)
{
Contract.Requires<ArgumentNullException>(device != null);
Contract.Requires<ArgumentNullException>(root != null);
Point = point;
Type = type;
InputModifiers = inputModifiers;
}
/// <summary>
/// Gets the pointer properties and position, in client DIPs.
/// </summary>
public RawPointerPoint Point
{
get => _point;
set => _point = value;
}
/// <summary>
/// Gets the mouse position, in client DIPs.
/// </summary>
public Point Position { get; set; }
public Point Position
{
get => _point.Position;
set => _point.Position = value;
}
/// <summary>
/// Gets the type of the event.
@ -78,6 +119,19 @@ namespace Avalonia.Input.Raw
/// Points that were traversed by a pointer since the previous relevant event,
/// only valid for Move and TouchUpdate
/// </summary>
public IReadOnlyList<Point>? IntermediatePoints { get; set; }
public Lazy<IReadOnlyList<RawPointerPoint>?>? IntermediatePoints { get; set; }
}
public struct RawPointerPoint
{
/// <summary>
/// Pointer position, in client DIPs.
/// </summary>
public Point Position { get; set; }
public RawPointerPoint()
{
Position = default;
}
}
}

53
src/Avalonia.MicroCom/CallbackBase.cs

@ -0,0 +1,53 @@
namespace Avalonia.MicroCom
{
public abstract class CallbackBase : IUnknown, IMicroComShadowContainer
{
private readonly object _lock = new object();
private bool _referencedFromManaged = true;
private bool _referencedFromNative = false;
private bool _destroyed;
public bool IsDestroyed => _destroyed;
protected virtual void Destroyed()
{
}
public void Dispose()
{
lock (_lock)
{
_referencedFromManaged = false;
DestroyIfNeeded();
}
}
void DestroyIfNeeded()
{
if(_destroyed)
return;
if (_referencedFromManaged == false && _referencedFromNative == false)
{
_destroyed = true;
Destroyed();
}
}
public MicroComShadow Shadow { get; set; }
public void OnReferencedFromNative()
{
lock (_lock)
_referencedFromNative = true;
}
public void OnUnreferencedFromNative()
{
lock (_lock)
{
_referencedFromNative = false;
DestroyIfNeeded();
}
}
}
}

15
src/Avalonia.MicroCom/MicroComRuntime.cs

@ -12,6 +12,8 @@ namespace Avalonia.MicroCom
new ConcurrentDictionary<Type, Func<IntPtr, bool, object>>();
private static ConcurrentDictionary<Type, Guid> _guids = new ConcurrentDictionary<Type, Guid>();
private static ConcurrentDictionary<Guid, Type> _guidsToTypes = new ConcurrentDictionary<Guid, Type>();
internal static readonly Guid ManagedObjectInterfaceGuid = Guid.Parse("cd7687c0-a9c2-4563-b08e-a399df50c633");
static MicroComRuntime()
{
@ -82,6 +84,19 @@ namespace Avalonia.MicroCom
return shadow.Target;
}
public static bool IsComWrapper(IUnknown obj) => obj is MicroComProxyBase;
public static object TryUnwrapManagedObject(IUnknown obj)
{
if (obj is not MicroComProxyBase proxy)
return null;
if (proxy.QueryInterface(ManagedObjectInterfaceGuid, out _) != 0)
return null;
// Successful QueryInterface always increments ref counter
proxy.Release();
return GetObjectFromCcw(proxy.NativePointer);
}
public static bool TryGetTypeForGuid(Guid guid, out Type t) => _guidsToTypes.TryGetValue(guid, out t);
public static bool GetVtableFor(Type type, out IntPtr ptr) => _vtables.TryGetValue(type, out ptr);

6
src/Avalonia.MicroCom/MicroComShadow.cs

@ -23,6 +23,12 @@ namespace Avalonia.MicroCom
{
if (MicroComRuntime.TryGetTypeForGuid(*guid, out var type))
return QueryInterface(type, ppv);
else if (*guid == MicroComRuntime.ManagedObjectInterfaceGuid)
{
ccw->RefCount++;
*ppv = ccw;
return 0;
}
else
return unchecked((int)0x80004002u);
}

2
src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs

@ -6,7 +6,7 @@ using Avalonia.Platform;
namespace Avalonia.Native
{
internal class AvaloniaNativeApplicationPlatform : CallbackBase, IAvnApplicationEvents, IPlatformLifetimeEventsImpl
internal class AvaloniaNativeApplicationPlatform : NativeCallbackBase, IAvnApplicationEvents, IPlatformLifetimeEventsImpl
{
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested;

2
src/Avalonia.Native/AvaloniaNativeDragSource.cs

@ -30,7 +30,7 @@ namespace Avalonia.Native
return visual.VisualRoot as TopLevel;
}
class DndCallback : CallbackBase, IAvnDndResultCallback
class DndCallback : NativeCallbackBase, IAvnDndResultCallback
{
private TaskCompletionSource<DragDropEffects> _tcs;

2
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -79,7 +79,7 @@ namespace Avalonia.Native
_factory = factory;
}
class GCHandleDeallocator : CallbackBase, IAvnGCHandleDeallocatorCallback
class GCHandleDeallocator : NativeCallbackBase, IAvnGCHandleDeallocatorCallback
{
public void FreeGCHandle(IntPtr handle)
{

49
src/Avalonia.Native/CallbackBase.cs

@ -5,19 +5,8 @@ using Avalonia.Platform;
namespace Avalonia.Native
{
public class CallbackBase : IUnknown, IMicroComShadowContainer, IMicroComExceptionCallback
public abstract class NativeCallbackBase : CallbackBase, IMicroComExceptionCallback
{
private readonly object _lock = new object();
private bool _referencedFromManaged = true;
private bool _referencedFromNative = false;
private bool _destroyed;
protected virtual void Destroyed()
{
}
public void RaiseException(Exception e)
{
if (AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>() is PlatformThreadingInterface threadingInterface)
@ -27,41 +16,5 @@ namespace Avalonia.Native
threadingInterface.DispatchException(ExceptionDispatchInfo.Capture(e));
}
}
public void Dispose()
{
lock (_lock)
{
_referencedFromManaged = false;
DestroyIfNeeded();
}
}
void DestroyIfNeeded()
{
if(_destroyed)
return;
if (_referencedFromManaged == false && _referencedFromNative == false)
{
_destroyed = true;
Destroyed();
}
}
public MicroComShadow Shadow { get; set; }
public void OnReferencedFromNative()
{
lock (_lock)
_referencedFromNative = true;
}
public void OnUnreferencedFromNative()
{
lock (_lock)
{
_referencedFromNative = false;
DestroyIfNeeded();
}
}
}
}

2
src/Avalonia.Native/DeferredFramebuffer.cs

@ -27,7 +27,7 @@ namespace Avalonia.Native
public Vector Dpi { get; set; }
public PixelFormat Format { get; set; }
class Disposer : CallbackBase
class Disposer : NativeCallbackBase
{
private IntPtr _ptr;

2
src/Avalonia.Native/IAvnMenu.cs

@ -7,7 +7,7 @@ using Avalonia.Platform.Interop;
namespace Avalonia.Native.Interop
{
class MenuEvents : CallbackBase, IAvnMenuEvents
class MenuEvents : NativeCallbackBase, IAvnMenuEvents
{
private IAvnMenu _parent;

2
src/Avalonia.Native/MenuActionCallback.cs

@ -3,7 +3,7 @@ using Avalonia.Native.Interop;
namespace Avalonia.Native
{
public class MenuActionCallback : CallbackBase, IAvnActionCallback
public class MenuActionCallback : NativeCallbackBase, IAvnActionCallback
{
private Action _action;

4
src/Avalonia.Native/PlatformThreadingInterface.cs

@ -9,7 +9,7 @@ namespace Avalonia.Native
{
internal class PlatformThreadingInterface : IPlatformThreadingInterface
{
class TimerCallback : CallbackBase, IAvnActionCallback
class TimerCallback : NativeCallbackBase, IAvnActionCallback
{
readonly Action _tick;
@ -24,7 +24,7 @@ namespace Avalonia.Native
}
}
class SignaledCallback : CallbackBase, IAvnSignaledCallback
class SignaledCallback : NativeCallbackBase, IAvnSignaledCallback
{
readonly PlatformThreadingInterface _parent;

2
src/Avalonia.Native/PredicateCallback.cs

@ -3,7 +3,7 @@ using Avalonia.Native.Interop;
namespace Avalonia.Native
{
public class PredicateCallback : CallbackBase, IAvnPredicateCallback
public class PredicateCallback : NativeCallbackBase, IAvnPredicateCallback
{
private Func<bool> _predicate;

2
src/Avalonia.Native/SystemDialogs.cs

@ -62,7 +62,7 @@ namespace Avalonia.Native
}
}
internal unsafe class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents
internal unsafe class SystemDialogEvents : NativeCallbackBase, IAvnSystemDialogEvents
{
private TaskCompletionSource<string[]> _tcs;

2
src/Avalonia.Native/WindowImplBase.cs

@ -151,7 +151,7 @@ namespace Avalonia.Native
public IMouseDevice MouseDevice => _mouse;
public abstract IPopupImpl CreatePopup();
protected unsafe class WindowBaseEvents : CallbackBase, IAvnWindowBaseEvents
protected unsafe class WindowBaseEvents : NativeCallbackBase, IAvnWindowBaseEvents
{
private readonly WindowBaseImpl _parent;

2
src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs

@ -201,7 +201,7 @@ namespace Avalonia.PlatformSupport
#if NETCOREAPP
IsCoreClr = true,
#elif NETFRAMEWORK
IsDotNetFramework = false,
IsDotNetFramework = true,
#endif
IsDesktop = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.WinNT,
IsMono = os == OperatingSystemType.Android || os == OperatingSystemType.iOS || os == OperatingSystemType.Browser,

1
src/Avalonia.ReactiveUI/ReactiveUserControl.cs

@ -48,6 +48,7 @@ namespace Avalonia.ReactiveUI
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
ViewModel = DataContext as TViewModel;
}

1
src/Avalonia.ReactiveUI/TransitioningContentControl.cs

@ -10,6 +10,7 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// A ContentControl that animates the transition when its content is changed.
/// </summary>
[Obsolete("Use TransitioningContentControl in Avalonia.Controls namespace")]
public class TransitioningContentControl : ContentControl, IStyleable
{
/// <summary>

271
src/Avalonia.Themes.Default/Controls/SplitButton.xaml

@ -0,0 +1,271 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True"
xmlns:converters="using:Avalonia.Controls.Converters">
<!-- This style is heavily based on the Fluent v1 style.
The base resources are switched out along with a few minor changes such as Padding, CornerRadius. -->
<Design.PreviewWith>
<Border Padding="20">
<SplitButton />
</Border>
</Design.PreviewWith>
<Styles.Resources>
<x:Double x:Key="SplitButtonPrimaryButtonSize">24</x:Double>
<x:Double x:Key="SplitButtonSecondaryButtonSize">24</x:Double>
<x:Double x:Key="SplitButtonSeparatorWidth">1</x:Double>
<Thickness x:Key="SplitButtonBorderThemeThickness">1</Thickness>
<converters:MarginMultiplierConverter x:Key="PrimaryButtonBorderMultiplier" Left="True" Top="True" Bottom="True" Indent="1" />
<converters:MarginMultiplierConverter x:Key="SecondaryButtonBorderMultiplier" Right="True" Top="True" Bottom="True" Indent="1" />
<converters:MarginMultiplierConverter x:Key="SeparatorBorderMultiplier" Top="True" Bottom="True" Indent="1" />
<converters:CornerRadiusFilterConverter x:Key="TopCornerRadiusFilterConverter" Filter="TopLeft, TopRight"/>
<converters:CornerRadiusFilterConverter x:Key="RightCornerRadiusFilterConverter" Filter="TopRight, BottomRight"/>
<converters:CornerRadiusFilterConverter x:Key="BottomCornerRadiusFilterConverter" Filter="BottomLeft, BottomRight"/>
<converters:CornerRadiusFilterConverter x:Key="LeftCornerRadiusFilterConverter" Filter="TopLeft, BottomLeft"/>
<SolidColorBrush x:Key="SplitButtonBackground" Color="{DynamicResource ThemeControlMidColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundPointerOver" Color="{DynamicResource ThemeControlMidColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundPressed" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundDisabled" Color="{DynamicResource ThemeControlMidColor}" Opacity="{DynamicResource ThemeDisabledOpacity}" />
<SolidColorBrush x:Key="SplitButtonBackgroundChecked" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled" Color="{DynamicResource ThemeControlHighColor}" Opacity="{DynamicResource ThemeDisabledOpacity}" />
<SolidColorBrush x:Key="SplitButtonForeground" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundPointerOver" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundPressed" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundDisabled" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundChecked" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundCheckedPointerOver" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundCheckedPressed" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundCheckedDisabled" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrush" Color="{DynamicResource ThemeBorderLowColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushPointerOver" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushPressed" Color="{DynamicResource ThemeBorderLowColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushDisabled" Color="{DynamicResource ThemeBorderLowColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushChecked" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushCheckedPointerOver" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushCheckedPressed" Color="{DynamicResource ThemeControlHighColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushCheckedDisabled" Color="{DynamicResource ThemeControlMidColor}" Opacity="{DynamicResource ThemeDisabledOpacity}" />
</Styles.Resources>
<Style Selector="SplitButton">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource SplitButtonBorderThemeThickness}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<!--<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="FocusVisualMargin" Value="-3" />-->
<Setter Property="KeyboardNavigation.IsTabStop" Value="True" />
<Setter Property="Focusable" Value="True" />
<Setter Property="Padding" Value="4" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Name="PART_PrimaryButton"
Grid.Column="0"
MinWidth="{DynamicResource SplitButtonPrimaryButtonSize}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource PrimaryButtonBorderMultiplier}}"
BorderBrush="{TemplateBinding BorderBrush}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
Focusable="False"
KeyboardNavigation.IsTabStop="False" />
<Border x:Name="SeparatorBorder"
Grid.Column="1"
Background="Transparent"
Width="{DynamicResource SplitButtonSeparatorWidth}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource SeparatorBorderMultiplier}}"
BorderBrush="{TemplateBinding BorderBrush}" />
<Button x:Name="PART_SecondaryButton"
Grid.Column="2"
MinWidth="{DynamicResource SplitButtonSecondaryButtonSize}"
BorderBrush="{TemplateBinding BorderBrush}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource SecondaryButtonBorderMultiplier}}"
Padding="0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Focusable="False"
KeyboardNavigation.IsTabStop="False" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Default overridable styles -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}" />
<Setter Property="Content">
<Template>
<PathIcon Height="12"
Width="12"
Data="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z" />
</Template>
</Setter>
</Style>
<!-- Primary and Secondary buttons PointerOver State -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton:pointerover /template/ ContentPresenter,
SplitButton /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPointerOver}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<!-- Primary and Secondary buttons Pressed State -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton:pressed /template/ ContentPresenter,
SplitButton /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- SplitButton Pressed State -->
<Style Selector="SplitButton:pressed /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:pressed /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:pressed /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:pressed /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- Primary and Secondary buttons Flyout Open State -->
<Style Selector="SplitButton:flyout-open /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:flyout-open /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:flyout-open /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- Disabled State -->
<Style Selector="SplitButton:disabled /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:disabled /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:disabled /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundDisabled}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<Style Selector="SplitButton:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<!-- Checked State -->
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:checked /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<!-- Checked PointerOver State -->
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton:pointerover /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPointerOver}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<!-- Checked Pressed State -->
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton:pressed /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- SplitButton Checked Pressed State -->
<Style Selector="SplitButton:pressed:checked /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:pressed:checked /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:pressed:checked /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:pressed:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- Checked Flyout Open State -->
<Style Selector="SplitButton:checked:flyout-open /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked:flyout-open /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:checked:flyout-open /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- Disabled Checked State -->
<Style Selector="SplitButton:checked:disabled /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked:disabled /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:checked:disabled /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedDisabled}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
<Style Selector="SplitButton:checked:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
</Styles>

20
src/Avalonia.Themes.Default/Controls/TransitioningContentControl.xaml

@ -0,0 +1,20 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="TransitioningContentControl">
<!-- Set Defaults -->
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding CurrentContent}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>
</Styles>

2
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -38,6 +38,7 @@
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TabItem.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TextBox.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/ToggleButton.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TransitioningContentControl.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/Expander.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TitleBar.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TreeView.xaml"/>
@ -57,6 +58,7 @@
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/NotificationCard.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/NativeMenuBar.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/ToggleSwitch.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/SplitButton.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/SplitView.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/DatePicker.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TimePicker.xaml"/>

27
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml

@ -556,6 +556,33 @@
<Thickness x:Key="TreeViewItemBorderThemeThickness">1</Thickness>
<x:Double x:Key="TreeViewItemMinHeight">32</x:Double>
<!-- Resources for SplitButton.xaml (shared with ToggleSplitButton) -->
<StaticResource x:Key="SplitButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="SplitButtonBackgroundPointerOver" Color="{StaticResource SystemBaseHighColor}" Opacity="0.1" />
<StaticResource x:Key="SplitButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="SplitButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SplitButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="SplitButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColorDark1" />
<StaticResource x:Key="SplitButtonBackgroundCheckedPressed" ResourceKey="SystemAccentColorLight1" />
<StaticResource x:Key="SplitButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SplitButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="SplitButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="SplitButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="SplitButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="SplitButtonForegroundChecked" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="SplitButtonForegroundCheckedPointerOver" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="SplitButtonForegroundCheckedPressed" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="SplitButtonForegroundCheckedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="SplitButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushPointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushChecked" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushCheckedPointerOver" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushCheckedPressed" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushCheckedDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<Thickness x:Key="SplitButtonBorderThemeThickness">1</Thickness>
<!-- Resources for SplitView.xaml -->
<StaticResource x:Key="SplitViewLightDismissOverlayBackground" ResourceKey="SystemControlPageBackgroundMediumAltMediumBrush" />

27
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml

@ -550,6 +550,33 @@
<Thickness x:Key="TreeViewItemBorderThemeThickness">1</Thickness>
<x:Double x:Key="TreeViewItemMinHeight">32</x:Double>
<!-- Resources for SplitButton.xaml (shared with ToggleSplitButton) -->
<StaticResource x:Key="SplitButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="SplitButtonBackgroundPointerOver" Color="{StaticResource SystemBaseHighColor}" Opacity="0.1" />
<StaticResource x:Key="SplitButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="SplitButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SplitButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="SplitButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColorLight1" />
<StaticResource x:Key="SplitButtonBackgroundCheckedPressed" ResourceKey="SystemAccentColorDark1" />
<StaticResource x:Key="SplitButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SplitButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="SplitButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="SplitButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="SplitButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="SplitButtonForegroundChecked" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="SplitButtonForegroundCheckedPointerOver" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="SplitButtonForegroundCheckedPressed" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="SplitButtonForegroundCheckedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="SplitButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushPointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushChecked" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushCheckedPointerOver" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushCheckedPressed" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="SplitButtonBorderBrushCheckedDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<Thickness x:Key="SplitButtonBorderThemeThickness">1</Thickness>
<!-- Resources for SplitView.xaml -->
<StaticResource x:Key="SplitViewLightDismissOverlayBackground" ResourceKey="SystemControlPageBackgroundMediumAltMediumBrush" />

2
src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml

@ -150,7 +150,7 @@
</Setter>
</Style>
<!-- NormalState -->
<!-- Normal State -->
<Style Selector="ComboBox /template/ TextBlock#PlaceholderTextBlock">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxPlaceHolderForeground}" />
</Style>

2
src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml

@ -37,6 +37,7 @@
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabItem.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TextBox.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToggleButton.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/Expander.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TitleBar.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TreeView.xaml"/>
@ -56,6 +57,7 @@
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/NotificationCard.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/SplitButton.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/SplitView.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/DatePicker.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TimePicker.xaml"/>

247
src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml

@ -0,0 +1,247 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True"
xmlns:converters="using:Avalonia.Controls.Converters">
<Design.PreviewWith>
<Border Padding="60">
<StackPanel>
<SplitButton Content="Hello World">
<SplitButton.Flyout>
<Flyout>Hello</Flyout>
</SplitButton.Flyout>
</SplitButton>
<ToggleSplitButton Content="Hello World">
<ToggleSplitButton.Flyout>
<Flyout>Hello</Flyout>
</ToggleSplitButton.Flyout>
</ToggleSplitButton>
</StackPanel>
</Border>
</Design.PreviewWith>
<Styles.Resources>
<x:Double x:Key="SplitButtonPrimaryButtonSize">32</x:Double>
<x:Double x:Key="SplitButtonSecondaryButtonSize">32</x:Double>
<x:Double x:Key="SplitButtonSeparatorWidth">1</x:Double>
<converters:MarginMultiplierConverter x:Key="PrimaryButtonBorderMultiplier" Left="True" Top="True" Bottom="True" Indent="1" />
<converters:MarginMultiplierConverter x:Key="SecondaryButtonBorderMultiplier" Right="True" Top="True" Bottom="True" Indent="1" />
<converters:MarginMultiplierConverter x:Key="SeparatorBorderMultiplier" Top="True" Bottom="True" Indent="1" />
</Styles.Resources>
<Style Selector="SplitButton">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource SplitButtonBorderThemeThickness}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<!--<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="FocusVisualMargin" Value="-3" />-->
<Setter Property="KeyboardNavigation.IsTabStop" Value="True" />
<Setter Property="Focusable" Value="True" />
<Setter Property="Padding" Value="{DynamicResource ButtonPadding}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Name="PART_PrimaryButton"
Grid.Column="0"
MinWidth="{DynamicResource SplitButtonPrimaryButtonSize}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource PrimaryButtonBorderMultiplier}}"
BorderBrush="{TemplateBinding BorderBrush}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
Focusable="False"
KeyboardNavigation.IsTabStop="False" />
<Border x:Name="SeparatorBorder"
Grid.Column="1"
Background="Transparent"
Width="{DynamicResource SplitButtonSeparatorWidth}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource SeparatorBorderMultiplier}}"
BorderBrush="{TemplateBinding BorderBrush}" />
<Button x:Name="PART_SecondaryButton"
Grid.Column="2"
MinWidth="{DynamicResource SplitButtonSecondaryButtonSize}"
BorderBrush="{TemplateBinding BorderBrush}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource SecondaryButtonBorderMultiplier}}"
Padding="0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Focusable="False"
KeyboardNavigation.IsTabStop="False" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Default overridable styles -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}" />
<Setter Property="Content">
<Template>
<PathIcon Height="12"
Width="12"
Data="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z" />
</Template>
</Setter>
</Style>
<!-- Primary and Secondary buttons PointerOver State -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton:pointerover /template/ ContentPresenter,
SplitButton /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPointerOver}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<!-- Primary and Secondary buttons Pressed State -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton:pressed /template/ ContentPresenter,
SplitButton /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- SplitButton Pressed State -->
<Style Selector="SplitButton:pressed /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:pressed /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:pressed /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:pressed /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- Primary and Secondary buttons Flyout Open State -->
<Style Selector="SplitButton:flyout-open /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:flyout-open /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:flyout-open /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- Disabled State -->
<Style Selector="SplitButton:disabled /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:disabled /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:disabled /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundDisabled}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<Style Selector="SplitButton:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<!-- Checked State -->
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:checked /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<!-- Checked PointerOver State -->
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton:pointerover /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPointerOver}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<!-- Checked Pressed State -->
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton:pressed /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- SplitButton Checked Pressed State -->
<Style Selector="SplitButton:pressed:checked /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:pressed:checked /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:pressed:checked /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:pressed:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- Checked Flyout Open State -->
<Style Selector="SplitButton:checked:flyout-open /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked:flyout-open /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:checked:flyout-open /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- Disabled Checked State -->
<Style Selector="SplitButton:checked:disabled /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked:disabled /template/ Button#PART_SecondaryButton /template/ ContentPresenter,
SplitButton:checked:disabled /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedDisabled}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
<Style Selector="SplitButton:checked:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
</Styles>

20
src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml

@ -0,0 +1,20 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="TransitioningContentControl">
<!-- Set Defaults -->
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding CurrentContent}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>
</Styles>

6
src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs

@ -12,6 +12,8 @@ namespace Avalonia.Animation.Animators
/// </summary>
public class ISolidColorBrushAnimator : Animator<ISolidColorBrush?>
{
private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator();
public override ISolidColorBrush? Interpolate(double progress, ISolidColorBrush? oldValue, ISolidColorBrush? newValue)
{
if (oldValue is null || newValue is null)
@ -19,7 +21,9 @@ namespace Avalonia.Animation.Animators
return progress >= 0.5 ? newValue : oldValue;
}
return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
return new ImmutableSolidColorBrush(
ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color),
s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity));
}
public override IDisposable BindAnimation(Animatable control, IObservable<ISolidColorBrush?> instance)

2
src/Avalonia.Visuals/Animation/PageSlide.cs

@ -79,6 +79,7 @@ namespace Avalonia.Animation
var animation = new Animation
{
Easing = SlideOutEasing,
FillMode = FillMode.Forward,
Children =
{
new KeyFrame
@ -109,6 +110,7 @@ namespace Avalonia.Animation
to.IsVisible = true;
var animation = new Animation
{
FillMode = FillMode.Forward,
Easing = SlideInEasing,
Children =
{

26
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@ -194,6 +194,7 @@ namespace Avalonia.Media.TextFormatting
var currentY = 0d;
var currentPosition = 0;
var currentRect = Rect.Empty;
foreach (var textLine in TextLines)
{
@ -209,7 +210,10 @@ namespace Avalonia.Media.TextFormatting
//The whole line is covered.
if (currentPosition >= start && start + length > currentPosition + textLine.TextRange.Length)
{
result.Add(new Rect(textLine.Start, currentY, textLine.WidthIncludingTrailingWhitespace, textLine.Height));
currentRect = new Rect(textLine.Start, currentY, textLine.WidthIncludingTrailingWhitespace,
textLine.Height);
result.Add(currentRect);
currentY += textLine.Height;
currentPosition += textLine.TextRange.Length;
@ -317,13 +321,23 @@ namespace Avalonia.Media.TextFormatting
var width = endX - startX;
result.Add(new Rect(startX, currentY, width, textLine.Height));
if (result.Count > 0 && MathUtilities.AreClose(currentRect.Top, currentY) &&
MathUtilities.AreClose(currentRect.Right, startX))
{
result[result.Count - 1] = currentRect.WithWidth(currentRect.Width + width);
}
else
{
currentRect = new Rect(startX, currentY, width, textLine.Height);
result.Add(currentRect);
}
if (currentRun.ShapedBuffer.IsLeftToRight)
{
if (nextRun != null)
{
if (nextRun.Text.Start > currentRun.Text.Start)
if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= start + length)
{
break;
}
@ -397,7 +411,7 @@ namespace Avalonia.Media.TextFormatting
return new TextHitTestResult();
}
characterHit = currentLine.GetNextCaretCharacterHit(new CharacterHit(currentLine.TextRange.End));
characterHit = currentLine.GetCharacterHitFromDistance(point.X);
return GetHitTestResult(currentLine, characterHit, point);
}
@ -424,7 +438,7 @@ namespace Avalonia.Media.TextFormatting
continue;
}
if (charIndex >= textLine.Start && charIndex <= textLine.TextRange.End + (trailingEdge ? 1 : 0))
if (charIndex >= textLine.TextRange.Start && charIndex <= textLine.TextRange.End + (trailingEdge ? 1 : 0))
{
return index;
}

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

Loading…
Cancel
Save