Browse Source

Merge branch 'master' into serilog-improvements

pull/2498/head
Jumar Macato 7 years ago
committed by GitHub
parent
commit
4da8d3d4ff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .gitmodules
  2. 6
      build/EmbedXaml.props
  3. 5
      build/SampleApp.props
  4. 6
      dirs.proj
  5. 2
      nukebuild/Numerge
  6. 39
      packages/Avalonia/AvaloniaBuildTasks.targets
  7. 1
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  8. 2
      samples/ControlCatalog/App.xaml
  9. 1
      samples/ControlCatalog/MainView.xaml
  10. 23
      samples/ControlCatalog/MainView.xaml.cs
  11. 12
      samples/ControlCatalog/MainWindow.xaml
  12. 15
      samples/ControlCatalog/MainWindow.xaml.cs
  13. 9
      samples/ControlCatalog/Pages/ButtonPage.xaml.cs
  14. 2
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs
  15. 10
      samples/ControlCatalog/Pages/NotificationsPage.xaml
  16. 18
      samples/ControlCatalog/Pages/NotificationsPage.xaml.cs
  17. 6
      samples/ControlCatalog/SideBar.xaml
  18. 44
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  19. 30
      samples/ControlCatalog/ViewModels/NotificationViewModel.cs
  20. 19
      samples/ControlCatalog/Views/CustomNotificationView.xaml
  21. 18
      samples/ControlCatalog/Views/CustomNotificationView.xaml.cs
  22. 21
      src/Avalonia.Base/AvaloniaProperty.cs
  23. 5
      src/Avalonia.Base/Data/Core/ExpressionParseException.cs
  24. 10
      src/Avalonia.Base/Metadata/UsableDuringInitializationAttribute.cs
  25. 10
      src/Avalonia.Base/Platform/IAssetLoader.cs
  26. 30
      src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs
  27. 5
      src/Avalonia.Base/Utilities/CharacterReader.cs
  28. 5
      src/Avalonia.Base/Utilities/IdentifierParser.cs
  29. 62
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  30. 73
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  31. 3
      src/Avalonia.Build.Tasks/Extensions.cs
  32. 61
      src/Avalonia.Build.Tasks/Program.cs
  33. 73
      src/Avalonia.Build.Tasks/SpanCompat.cs
  34. 149
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
  35. 357
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  36. 6
      src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
  37. 1
      src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs
  38. 50
      src/Avalonia.Controls/LayoutTransformControl.cs
  39. 23
      src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs
  40. 44
      src/Avalonia.Controls/Notifications/INotification.cs
  41. 18
      src/Avalonia.Controls/Notifications/INotificationManager.cs
  42. 60
      src/Avalonia.Controls/Notifications/Notification.cs
  43. 160
      src/Avalonia.Controls/Notifications/NotificationCard.cs
  44. 17
      src/Avalonia.Controls/Notifications/NotificationPosition.cs
  45. 16
      src/Avalonia.Controls/Notifications/NotificationType.cs
  46. 64
      src/Avalonia.Controls/Notifications/ReversibleStackPanel.cs
  47. 163
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  48. 2
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  49. 9
      src/Avalonia.Controls/Primitives/AdornerDecorator.cs
  50. 6
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  51. 3
      src/Avalonia.Controls/Properties/AssemblyInfo.cs
  52. 5
      src/Avalonia.Controls/StackPanel.cs
  53. 3
      src/Avalonia.Controls/TopLevel.cs
  54. 8
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  55. 1
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  56. 4
      src/Avalonia.Diagnostics/DevTools.xaml
  57. 6
      src/Avalonia.Diagnostics/DevTools.xaml.cs
  58. 3
      src/Avalonia.Diagnostics/Views/EventsView.xaml
  59. 4
      src/Avalonia.Diagnostics/Views/TreePageView.xaml
  60. 3
      src/Avalonia.Styling/Controls/NameScopeExtensions.cs
  61. 11
      src/Avalonia.Styling/Styling/Selectors.cs
  62. 8
      src/Avalonia.Themes.Default/Accents/BaseDark.xaml
  63. 8
      src/Avalonia.Themes.Default/Accents/BaseLight.xaml
  64. 6
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  65. 9
      src/Avalonia.Themes.Default/DatePicker.xaml
  66. 6
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  67. 7
      src/Avalonia.Themes.Default/DefaultTheme.xaml.cs
  68. 2
      src/Avalonia.Themes.Default/MenuItem.xaml
  69. 88
      src/Avalonia.Themes.Default/NotificationCard.xaml
  70. 10
      src/Avalonia.Themes.Default/TabItem.xaml
  71. 45
      src/Avalonia.Themes.Default/WindowNotificationManager.xaml
  72. 110
      src/Avalonia.Visuals/Media/FontFamily.cs
  73. 35
      src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
  74. 2
      src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
  75. 2
      src/Avalonia.Visuals/Visual.cs
  76. 6
      src/Avalonia.X11/XIStructs.cs
  77. 32
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  78. 1
      src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
  79. 35
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  80. 4
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  81. 7
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs
  82. 4
      src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs
  83. 2
      src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs
  84. 2
      src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs
  85. 61
      src/Markup/Avalonia.Markup.Xaml/Extensions.cs
  86. 11
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  87. 20
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  88. 7
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
  89. 34
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  90. 8
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs
  91. 4
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs
  92. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
  93. 5
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  94. 7
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  95. 276
      src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs
  96. 124
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  97. 176
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  98. 94
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs
  99. 20
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaBindingExtensionHackTransformer.cs
  100. 24
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs

3
.gitmodules

@ -4,3 +4,6 @@
[submodule "nukebuild/Numerge"]
path = nukebuild/Numerge
url = https://github.com/kekekeks/Numerge.git
[submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"]
path = src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
url = https://github.com/kekekeks/XamlIl.git

6
build/EmbedXaml.props

@ -4,8 +4,8 @@
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<EmbeddedResource Include="**\*.xaml">
<AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
</AvaloniaResource>
</ItemGroup>
</Project>
</Project>

5
build/SampleApp.props

@ -5,4 +5,9 @@
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
</ItemGroup>
<Target Name="GatherReferences" AfterTargets="CoreCompile">
<WriteLinesToFile File="$(TargetPath).refs"
Lines="@(ReferencePathWithRefAssemblies)"
Overwrite="true" />
</Target>
</Project>

6
dirs.proj

@ -6,12 +6,12 @@
<ProjectReference Include="packages/**/*.*proj" />
<ProjectReference Remove="**/*.shproj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/XamlIl/**/*.*proj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">
<!-- Disabled on CI because of ancient MSBuild project format -->
<ItemGroup>
<ProjectReference Remove="src/Android/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Android/ControlCatalog.Android.csproj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\iOS')">
<ProjectReference Remove="src/iOS/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj" />
</ItemGroup>

2
nukebuild/Numerge

@ -1 +1 @@
Subproject commit aef10ae67dc55c95f49b52a505a0be33bfa297a5
Subproject commit 4464343aef5c8ab7a42fcb20a483a6058199f8b8

39
packages/Avalonia/AvaloniaBuildTasks.targets

@ -8,6 +8,10 @@
AssemblyFile="$(AvaloniaBuildTasksLocation)"
/>
<UsingTask TaskName="CompileAvaloniaXamlTask"
AssemblyFile="$(AvaloniaBuildTasksLocation)"
/>
<Target Name="AddAvaloniaResources" BeforeTargets="ResolveReferences">
<PropertyGroup>
@ -20,11 +24,15 @@
</ItemGroup>
</Target>
<PropertyGroup>
<BuildAvaloniaResourcesDependsOn>$(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences</BuildAvaloniaResourcesDependsOn>
</PropertyGroup>
<Target Name="GenerateAvaloniaResources"
BeforeTargets="CoreCompile;CoreResGen"
Inputs="@(AvaloniaResource);$(MSBuildAllProjects)"
Outputs="$(AvaloniaResourcesTemporaryFilePath)"
DependsOnTargets="$(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences">
DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)">
<GenerateAvaloniaResourcesTask
Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'"
Output="$(AvaloniaResourcesTemporaryFilePath)"
@ -33,9 +41,36 @@
EmbeddedResources="@(EmbeddedResources)"/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration)"/>
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/>
</Target>
<Target
Name="CompileAvaloniaXaml"
AfterTargets="AfterCompile"
Condition="Exists('@(IntermediateAssembly)') And $(DesignTimeBuild) != true And $(EnableAvaloniaXamlCompilation) != false"
>
<PropertyGroup>
<AvaloniaXamlReferencesTemporaryFilePath Condition="'$(AvaloniaXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/references</AvaloniaXamlReferencesTemporaryFilePath>
<AvaloniaXamlOriginalCopyFilePath Condition="'$(AvaloniaXamlOriginalCopyFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/original.dll</AvaloniaXamlOriginalCopyFilePath>
</PropertyGroup>
<WriteLinesToFile
Condition="'$(_AvaloniaForceInternalMSBuild)' != 'true'"
File="$(AvaloniaXamlReferencesTemporaryFilePath)"
Lines="@(ReferencePathWithRefAssemblies)"
Overwrite="true" />
<CompileAvaloniaXamlTask
Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'"
AssemblyFile="@(IntermediateAssembly)"
ReferencesFilePath="$(AvaloniaXamlReferencesTemporaryFilePath)"
OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)"
ProjectDirectory="$(MSBuildProjectDirectory)"
/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileAvaloniaXaml /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/>
</Target>
<ItemGroup>
<UpToDateCheckInput Include="@(AvaloniaResource)" />

1
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -3,6 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>
<ItemGroup>

2
samples/ControlCatalog/App.xaml

@ -4,7 +4,7 @@
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<StyleInclude Source="resm:Avalonia.Controls.DataGrid.Themes.Default.xaml?assembly=Avalonia.Controls.DataGrid"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="FontWeight" Value="Medium"/>

1
samples/ControlCatalog/MainView.xaml

@ -29,6 +29,7 @@
<TabItem Header="LayoutTransformControl"><pages:LayoutTransformControlPage/></TabItem>
<TabItem Header="ListBox"><pages:ListBoxPage/></TabItem>
<TabItem Header="Menu"><pages:MenuPage/></TabItem>
<TabItem Header="Notifications"><pages:NotificationsPage/></TabItem>
<TabItem Header="NumericUpDown"><pages:NumericUpDownPage/></TabItem>
<TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
<TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>

23
samples/ControlCatalog/MainView.xaml.cs

@ -1,8 +1,11 @@
using System;
using System.Collections;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Markup.Xaml.XamlIl;
using Avalonia.Platform;
using ControlCatalog.Pages;
@ -12,7 +15,7 @@ namespace ControlCatalog
{
public MainView()
{
this.InitializeComponent();
AvaloniaXamlLoader.Load(this);
if (AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().IsDesktop)
{
IList tabItems = ((IList)this.FindControl<TabControl>("Sidebar").Items);
@ -28,8 +31,17 @@ namespace ControlCatalog
});
}
var light = AvaloniaXamlLoader.Parse<StyleInclude>(@"<StyleInclude xmlns='https://github.com/avaloniaui' Source='resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default'/>");
var dark = AvaloniaXamlLoader.Parse<StyleInclude>(@"<StyleInclude xmlns='https://github.com/avaloniaui' Source='resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default'/>");
var light = new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default")
};
var dark = new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default")
};
var themes = this.Find<ComboBox>("Themes");
themes.SelectionChanged += (sender, e) =>
{
@ -45,10 +57,5 @@ namespace ControlCatalog
};
Styles.Add(light);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

12
samples/ControlCatalog/MainWindow.xaml

@ -1,8 +1,18 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
xmlns:pages="clr-namespace:ControlCatalog.Pages"
Title="Avalonia Control Gallery"
Icon="/Assets/test_icon.ico"
xmlns:local="clr-namespace:ControlCatalog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ControlCatalog.ViewModels"
xmlns:v="clr-namespace:ControlCatalog.Views"
x:Class="ControlCatalog.MainWindow">
<local:MainView/>
<Window.DataTemplates>
<DataTemplate DataType="vm:NotificationViewModel">
<v:CustomNotificationView />
</DataTemplate>
</Window.DataTemplates>
<Panel>
<local:MainView/>
</Panel>
</Window>

15
samples/ControlCatalog/MainWindow.xaml.cs

@ -1,18 +1,33 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using ControlCatalog.ViewModels;
using System;
using System.Threading.Tasks;
namespace ControlCatalog
{
public class MainWindow : Window
{
private WindowNotificationManager _notificationArea;
public MainWindow()
{
this.InitializeComponent();
this.AttachDevTools();
//Renderer.DrawFps = true;
//Renderer.DrawDirtyRects = Renderer.DrawFps = true;
_notificationArea = new WindowNotificationManager(this)
{
Position = NotificationPosition.TopRight,
MaxItems = 3
};
DataContext = new MainWindowViewModel(_notificationArea);
}
private void InitializeComponent()

9
samples/ControlCatalog/Pages/ButtonPage.xaml.cs

@ -5,14 +5,5 @@ namespace ControlCatalog.Pages
{
public class ButtonPage : UserControl
{
public ButtonPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

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

@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this);
}
private void OnSpin(object sender, SpinEventArgs e)
public void OnSpin(object sender, SpinEventArgs e)
{
var spinner = (ButtonSpinner)sender;
var txtBox = (TextBlock)spinner.Content;

10
samples/ControlCatalog/Pages/NotificationsPage.xaml

@ -0,0 +1,10 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.NotificationsPage">
<StackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Left">
<TextBlock Classes="h1">Notifications</TextBlock>
<Button Content="Show Standard Managed Notification" Command="{Binding ShowManagedNotificationCommand}" />
<Button Content="Show Custom Managed Notification" Command="{Binding ShowCustomManagedNotificationCommand}" />
<Button Content="Show Native Notification" Command="{Binding ShowNativeNotificationCommand}" />
</StackPanel>
</UserControl>

18
samples/ControlCatalog/Pages/NotificationsPage.xaml.cs

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class NotificationsPage : UserControl
{
public NotificationsPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

6
samples/ControlCatalog/SideBar.xaml

@ -1,6 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.SideBar">
>
<Design.PreviewWith>
<Border Padding="20">
<TabControl Classes="sidebar">
@ -63,13 +63,13 @@
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
</Style>
</Styles>

44
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@ -0,0 +1,44 @@
using System.Reactive;
using Avalonia.Controls.Notifications;
using Avalonia.Diagnostics.ViewModels;
using ReactiveUI;
namespace ControlCatalog.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
private IManagedNotificationManager _notificationManager;
public MainWindowViewModel(IManagedNotificationManager notificationManager)
{
_notificationManager = notificationManager;
ShowCustomManagedNotificationCommand = ReactiveCommand.Create(() =>
{
NotificationManager.Show(new NotificationViewModel(NotificationManager) { Title = "Hey There!", Message = "Did you know that Avalonia now supports Custom In-Window Notifications?" });
});
ShowManagedNotificationCommand = ReactiveCommand.Create(() =>
{
NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Welcome", "Avalonia now supports Notifications.", NotificationType.Information));
});
ShowNativeNotificationCommand = ReactiveCommand.Create(() =>
{
NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Error", "Native Notifications are not quite ready. Coming soon.", NotificationType.Error));
});
}
public IManagedNotificationManager NotificationManager
{
get { return _notificationManager; }
set { this.RaiseAndSetIfChanged(ref _notificationManager, value); }
}
public ReactiveCommand<Unit, Unit> ShowCustomManagedNotificationCommand { get; }
public ReactiveCommand<Unit, Unit> ShowManagedNotificationCommand { get; }
public ReactiveCommand<Unit, Unit> ShowNativeNotificationCommand { get; }
}
}

30
samples/ControlCatalog/ViewModels/NotificationViewModel.cs

@ -0,0 +1,30 @@
using System.Reactive;
using Avalonia.Controls.Notifications;
using ReactiveUI;
namespace ControlCatalog.ViewModels
{
public class NotificationViewModel
{
public NotificationViewModel(INotificationManager manager)
{
YesCommand = ReactiveCommand.Create(() =>
{
manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today."));
});
NoCommand = ReactiveCommand.Create(() =>
{
manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit..."));
});
}
public string Title { get; set; }
public string Message { get; set; }
public ReactiveCommand<Unit, Unit> YesCommand { get; }
public ReactiveCommand<Unit, Unit> NoCommand { get; }
}
}

19
samples/ControlCatalog/Views/CustomNotificationView.xaml

@ -0,0 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Views.CustomNotificationView">
<Border Padding="12" MinHeight="20" Background="DodgerBlue">
<Grid ColumnDefinitions="Auto,*">
<Panel Margin="0,0,12,0" Width="25" Height="25" VerticalAlignment="Top">
<TextBlock Text="&#xE115;" FontFamily="Segoe UI Symbol" FontSize="20" TextAlignment="Center" VerticalAlignment="Center"/>
</Panel>
<DockPanel Grid.Column="1">
<TextBlock DockPanel.Dock="Top" Text="{Binding Title}" FontWeight="Medium" />
<StackPanel Spacing="20" DockPanel.Dock="Bottom" Margin="0,8,0,0" Orientation="Horizontal">
<Button Content="No" DockPanel.Dock="Right" NotificationCard.CloseOnClick="True" Command="{Binding NoCommand}" Margin="0,0,8,0" />
<Button Content="Yes" DockPanel.Dock="Right" NotificationCard.CloseOnClick="True" Command="{Binding YesCommand}" />
</StackPanel>
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" Opacity=".8" Margin="0,8,0,0"/>
</DockPanel>
</Grid>
</Border>
</UserControl>

18
samples/ControlCatalog/Views/CustomNotificationView.xaml.cs

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Views
{
public class CustomNotificationView : UserControl
{
public CustomNotificationView()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

21
src/Avalonia.Base/AvaloniaProperty.cs

@ -19,7 +19,7 @@ namespace Avalonia
/// <summary>
/// Represents an unset property value.
/// </summary>
public static readonly object UnsetValue = new Unset();
public static readonly object UnsetValue = new UnsetValueType();
private static int s_nextId;
private readonly Subject<AvaloniaPropertyChangedEventArgs> _initialized;
@ -546,16 +546,17 @@ namespace Avalonia
}
}
}
/// <summary>
/// Class representing the <see cref="AvaloniaProperty.UnsetValue"/>.
/// </summary>
public class UnsetValueType
{
/// <summary>
/// Class representing the <see cref="UnsetValue"/>.
/// Returns the string representation of the <see cref="AvaloniaProperty.UnsetValue"/>.
/// </summary>
private class Unset
{
/// <summary>
/// Returns the string representation of the <see cref="UnsetValue"/>.
/// </summary>
/// <returns>The string "(unset)".</returns>
public override string ToString() => "(unset)";
}
/// <returns>The string "(unset)".</returns>
public override string ToString() => "(unset)";
}
}

5
src/Avalonia.Base/Data/Core/ExpressionParseException.cs

@ -9,7 +9,10 @@ namespace Avalonia.Data.Core
/// Exception thrown when <see cref="ExpressionObserver"/> could not parse the provided
/// expression string.
/// </summary>
public class ExpressionParseException : Exception
#if !BUILDTASK
public
#endif
class ExpressionParseException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionParseException"/> class.

10
src/Avalonia.Base/Metadata/UsableDuringInitializationAttribute.cs

@ -0,0 +1,10 @@
using System;
namespace Avalonia.Metadata
{
[AttributeUsage(AttributeTargets.Class)]
public class UsableDuringInitializationAttribute : Attribute
{
}
}

10
src/Avalonia.Base/Platform/IAssetLoader.cs

@ -61,6 +61,16 @@ namespace Avalonia.Platform
/// </exception>
(Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null);
/// <summary>
/// Extracts assembly information from URI
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>Assembly associated with the Uri</returns>
Assembly GetAssembly(Uri uri, Uri baseUri = null);
/// <summary>
/// Gets all assets of a folder and subfolders that match specified uri.
/// </summary>

30
src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs

@ -46,6 +46,36 @@ namespace Avalonia.Utilities
Entries = entries
});
}
public static byte[] Create(Dictionary<string, byte[]> data)
{
var sources = data.ToList();
var offsets = new Dictionary<string, int>();
var coffset = 0;
foreach (var s in sources)
{
offsets[s.Key] = coffset;
coffset += s.Value.Length;
}
var index = sources.Select(s => new AvaloniaResourcesIndexEntry
{
Path = s.Key,
Size = s.Value.Length,
Offset = offsets[s.Key]
}).ToList();
var output = new MemoryStream();
var ms = new MemoryStream();
AvaloniaResourcesIndexReaderWriter.Write(ms, index);
new BinaryWriter(output).Write((int)ms.Length);
ms.Position = 0;
ms.CopyTo(output);
foreach (var s in sources)
{
output.Write(s.Value,0,s.Value.Length);
}
return output.ToArray();
}
}
[DataContract]

5
src/Avalonia.Base/Utilities/CharacterReader.cs

@ -5,7 +5,10 @@ using System;
namespace Avalonia.Utilities
{
public ref struct CharacterReader
#if !BUILDTASK
public
#endif
ref struct CharacterReader
{
private ReadOnlySpan<char> _s;

5
src/Avalonia.Base/Utilities/IdentifierParser.cs

@ -6,7 +6,10 @@ using System.Globalization;
namespace Avalonia.Utilities
{
public static class IdentifierParser
#if !BUILDTASK
public
#endif
static class IdentifierParser
{
public static ReadOnlySpan<char> ParseIdentifier(this ref CharacterReader r)
{

62
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -1,9 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="$(Configuration) == 'Debug'">netstandard2.0;netcoreapp2.0</TargetFrameworks>
<OutputType>exe</OutputType>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK</DefineConstants>
<DefineConstants>$(DefineConstants);BUILDTASK;XAMLIL_CECIL_INTERNAL;XAMLIL_INTERNAL</DefineConstants>
<CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies>
<NoWarn>NU1605</NoWarn>
</PropertyGroup>
<ItemGroup>
@ -13,6 +17,54 @@
<Compile Include="../Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs">
<Link>Shared/AvaloniaResourceXamlInfo.cs</Link>
</Compile>
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" />
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/**/*.cs">
<Link>XamlIlExtensions/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="external/cecil/**/*.*" />
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlIl\**\*.cs">
<Link>XamlIl/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlIl.Cecil\**\*.cs">
<Link>XamlIl.Cecil/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Data/Core/ExpressionParseException.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Utilities/CharacterReader.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Utilities/IdentifierParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlIl\TypeSystem\SreTypeSystem.cs" />
<PackageReference Include="Avalonia.Unofficial.Cecil" Version="20190417.2.0" PrivateAssets="All" />
<PackageReference Condition="$(TargetFramework) == 'netstandard2.0'" Include="ILRepack.MSBuild.Task" Version="2.0.13" PrivateAssets="All" />
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" PrivateAssets="All" />
</ItemGroup>
<Target Name="ILRepack" AfterTargets="Build" Condition="$(TargetFramework) == 'netstandard2.0'">
<PropertyGroup>
<WorkingDirectory>$(MSBuildThisFileDirectory)bin\$(Configuration)\$(TargetFramework)</WorkingDirectory>
</PropertyGroup>
<ItemGroup>
<InputAssemblies Include="Mono.Cecil.dll" />
</ItemGroup>
<ILRepack OutputType="$(OutputType)" MainAssembly="$(AssemblyName).dll" OutputAssembly="$(AssemblyName).dll" InputAssemblies="@(InputAssemblies)" WorkingDirectory="$(WorkingDirectory)" />
<ItemGroup>
<DeleteNonNeededResults Include="$(WorkingDirectory)\*.dll" />
<DeleteNonNeededResults Remove="$(WorkingDirectory)\Avalonia.Build.Tasks.dll" />
</ItemGroup>
<Delete Files="@(DeleteNonNeededResults)" />
</Target>
</Project>

73
src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs

@ -0,0 +1,73 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.Build.Framework;
namespace Avalonia.Build.Tasks
{
public class CompileAvaloniaXamlTask: ITask
{
public bool Execute()
{
OutputPath = OutputPath ?? AssemblyFile;
var outputPdb = GetPdbPath(OutputPath);
var input = AssemblyFile;
var inputPdb = GetPdbPath(input);
// Make a copy and delete the original file to prevent MSBuild from thinking that everything is OK
if (OriginalCopyPath != null)
{
File.Copy(AssemblyFile, OriginalCopyPath, true);
input = OriginalCopyPath;
File.Delete(AssemblyFile);
if (File.Exists(inputPdb))
{
var copyPdb = GetPdbPath(OriginalCopyPath);
File.Copy(inputPdb, copyPdb, true);
File.Delete(inputPdb);
inputPdb = copyPdb;
}
}
var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath);
if (!res.Success)
return false;
if (!res.WrittenFile)
{
File.Copy(input, OutputPath, true);
if(File.Exists(inputPdb))
File.Copy(inputPdb, outputPdb, true);
}
return true;
}
string GetPdbPath(string p)
{
var d = Path.GetDirectoryName(p);
var f = Path.GetFileNameWithoutExtension(p);
var rv = f + ".pdb";
if (d != null)
rv = Path.Combine(d, rv);
return rv;
}
[Required]
public string AssemblyFile { get; set; }
[Required]
public string ReferencesFilePath { get; set; }
[Required]
public string OriginalCopyPath { get; set; }
[Required]
public string ProjectDirectory { get; set; }
public string OutputPath { get; set; }
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
}
}

3
src/Avalonia.Build.Tasks/Extensions.cs

@ -1,8 +1,9 @@
using System;
using Microsoft.Build.Framework;
namespace Avalonia.Build.Tasks
{
public static class Extensions
static class Extensions
{
static string FormatErrorCode(BuildEngineErrorCode code) => $"AVLN:{(int)code:0000}";

61
src/Avalonia.Build.Tasks/Program.cs

@ -0,0 +1,61 @@
using System;
using System.Collections;
using System.IO;
using Microsoft.Build.Framework;
namespace Avalonia.Build.Tasks
{
public class Program
{
static int Main(string[] args)
{
if (args.Length != 3)
{
Console.Error.WriteLine("input references output");
return 1;
}
return new CompileAvaloniaXamlTask()
{
AssemblyFile = args[0],
ReferencesFilePath = args[1],
OutputPath = args[2],
BuildEngine = new ConsoleBuildEngine(),
ProjectDirectory = Directory.GetCurrentDirectory()
}.Execute() ?
0 :
2;
}
class ConsoleBuildEngine : IBuildEngine
{
public void LogErrorEvent(BuildErrorEventArgs e)
{
Console.WriteLine($"ERROR: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogWarningEvent(BuildWarningEventArgs e)
{
Console.WriteLine($"WARNING: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogMessageEvent(BuildMessageEventArgs e)
{
Console.WriteLine($"MESSAGE: {e.Code} {e.Message} in {e.File} {e.LineNumber}:{e.ColumnNumber}-{e.EndLineNumber}:{e.EndColumnNumber}");
}
public void LogCustomEvent(CustomBuildEventArgs e)
{
Console.WriteLine($"CUSTOM: {e.Message}");
}
public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties,
IDictionary targetOutputs) => throw new NotSupportedException();
public bool ContinueOnError { get; }
public int LineNumberOfTaskNode { get; }
public int ColumnNumberOfTaskNode { get; }
public string ProjectFileOfTaskNode { get; }
}
}
}

73
src/Avalonia.Build.Tasks/SpanCompat.cs

@ -0,0 +1,73 @@
namespace System
{
// This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory
struct ReadOnlySpan<T>
{
private string _s;
private int _start;
private int _length;
public int Length => _length;
public ReadOnlySpan(string s) : this(s, 0, s.Length)
{
}
public ReadOnlySpan(string s, int start, int len)
{
_s = s;
_length = len;
_start = start;
if (_start > s.Length)
_length = 0;
else if (_start + _length > s.Length)
_length = s.Length - _start;
}
public char this[int c] => _s[_start + c];
public bool IsEmpty => _length == 0;
public ReadOnlySpan<char> Slice(int start, int len)
{
return new ReadOnlySpan<char>(_s, _start + start, len);
}
public static ReadOnlySpan<char> Empty => default;
public ReadOnlySpan<char> Slice(int start)
{
return new ReadOnlySpan<char>(_s, _start + start, _length - start);
}
public bool SequenceEqual(ReadOnlySpan<char> other)
{
if (_length != other.Length)
return false;
for(var c=0; c<_length;c++)
if (this[c] != other[c])
return false;
return true;
}
public ReadOnlySpan<char> TrimStart()
{
int start = 0;
for (; start < Length; start++)
{
if (!char.IsWhiteSpace(this[start]))
{
break;
}
}
return Slice(start);
}
public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
}
static class SpanCompatExtensions
{
public static ReadOnlySpan<char> AsSpan(this string s) => new ReadOnlySpan<char>(s);
}
}

149
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs

@ -0,0 +1,149 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Avalonia.Utilities;
using Mono.Cecil;
using Mono.Cecil.Cil;
using XamlIl.TypeSystem;
namespace Avalonia.Build.Tasks
{
public static partial class XamlCompilerTaskExecutor
{
interface IResource : IFileSource
{
string Uri { get; }
string Name { get; }
void Remove();
}
interface IResourceGroup
{
string Name { get; }
IEnumerable<IResource> Resources { get; }
}
class EmbeddedResources : IResourceGroup
{
private readonly AssemblyDefinition _asm;
public string Name => "EmbeddedResource";
public IEnumerable<IResource> Resources => _asm.MainModule.Resources.OfType<EmbeddedResource>()
.Select(r => new WrappedResource(_asm, r)).ToList();
public EmbeddedResources(AssemblyDefinition asm)
{
_asm = asm;
}
class WrappedResource : IResource
{
private readonly AssemblyDefinition _asm;
private readonly EmbeddedResource _res;
public WrappedResource(AssemblyDefinition asm, EmbeddedResource res)
{
_asm = asm;
_res = res;
}
public string Uri => $"resm:{Name}?assembly={_asm.Name.Name}";
public string Name => _res.Name;
public string FilePath => Name;
public byte[] FileContents => _res.GetResourceData();
public void Remove() => _asm.MainModule.Resources.Remove(_res);
}
}
class AvaloniaResources : IResourceGroup
{
private readonly AssemblyDefinition _asm;
Dictionary<string, AvaloniaResource> _resources = new Dictionary<string, AvaloniaResource>();
private EmbeddedResource _embedded;
public AvaloniaResources(AssemblyDefinition asm, string projectDir)
{
_asm = asm;
_embedded = ((EmbeddedResource)asm.MainModule.Resources.FirstOrDefault(r =>
r.ResourceType == ResourceType.Embedded && r.Name == "!AvaloniaResources"));
if (_embedded == null)
return;
using (var stream = _embedded.GetResourceStream())
{
var br = new BinaryReader(stream);
var index = AvaloniaResourcesIndexReaderWriter.Read(new MemoryStream(br.ReadBytes(br.ReadInt32())));
var baseOffset = stream.Position;
foreach (var e in index)
{
stream.Position = e.Offset + baseOffset;
_resources[e.Path] = new AvaloniaResource(this, projectDir, e.Path, br.ReadBytes(e.Size));
}
}
}
public void Save()
{
if (_embedded != null)
{
_asm.MainModule.Resources.Remove(_embedded);
_embedded = null;
}
if (_resources.Count == 0)
return;
_embedded = new EmbeddedResource("!AvaloniaResources", ManifestResourceAttributes.Public,
AvaloniaResourcesIndexReaderWriter.Create(_resources.ToDictionary(x => x.Key,
x => x.Value.FileContents)));
_asm.MainModule.Resources.Add(_embedded);
}
public string Name => "AvaloniaResources";
public IEnumerable<IResource> Resources => _resources.Values.ToList();
class AvaloniaResource : IResource
{
private readonly AvaloniaResources _grp;
private readonly byte[] _data;
public AvaloniaResource(AvaloniaResources grp,
string projectDir,
string name, byte[] data)
{
_grp = grp;
_data = data;
Name = name;
FilePath = Path.Combine(projectDir, name.TrimStart('/'));
Uri = $"avares://{grp._asm.Name.Name}/{name.TrimStart('/')}";
}
public string Uri { get; }
public string Name { get; }
public string FilePath { get; }
public byte[] FileContents => _data;
public void Remove() => _grp._resources.Remove(Name);
}
}
static void CopyDebugDocument(MethodDefinition method, MethodDefinition copyFrom)
{
if (!copyFrom.DebugInformation.HasSequencePoints)
return;
var dbg = method.DebugInformation;
dbg.Scope = new ScopeDebugInformation(method.Body.Instructions.First(), method.Body.Instructions.First())
{
End = new InstructionOffset(),
Import = new ImportDebugInformation()
};
dbg.SequencePoints.Add(new SequencePoint(method.Body.Instructions.First(),
copyFrom.DebugInformation.SequencePoints.First().Document)
{
StartLine = 0xfeefee,
EndLine = 0xfeefee
});
}
}
}

357
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -0,0 +1,357 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
using Microsoft.Build.Framework;
using Mono.Cecil;
using XamlIl.TypeSystem;
using Avalonia.Utilities;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using XamlIl;
using XamlIl.Ast;
using XamlIl.Parsers;
using XamlIl.Transform;
using FieldAttributes = Mono.Cecil.FieldAttributes;
using MethodAttributes = Mono.Cecil.MethodAttributes;
using TypeAttributes = Mono.Cecil.TypeAttributes;
namespace Avalonia.Build.Tasks
{
public static partial class XamlCompilerTaskExecutor
{
static bool CheckXamlName(IResource r) => r.Name.ToLowerInvariant().EndsWith(".xaml")
|| r.Name.ToLowerInvariant().EndsWith(".paml");
public class CompileResult
{
public bool Success { get; set; }
public bool WrittenFile { get; }
public CompileResult(bool success, bool writtenFile = false)
{
Success = success;
WrittenFile = writtenFile;
}
}
public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory,
string output)
{
var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input);
var asm = typeSystem.TargetAssemblyDefinition;
var emres = new EmbeddedResources(asm);
var avares = new AvaloniaResources(asm, projectDirectory);
if (avares.Resources.Count(CheckXamlName) == 0 && emres.Resources.Count(CheckXamlName) == 0)
// Nothing to do
return new CompileResult(true);
var xamlLanguage = AvaloniaXamlIlLanguage.Configure(typeSystem);
var compilerConfig = new XamlIlTransformerConfiguration(typeSystem,
typeSystem.TargetAssembly,
xamlLanguage,
XamlIlXmlnsMappings.Resolve(typeSystem, xamlLanguage),
AvaloniaXamlIlLanguage.CustomValueConverter);
var contextDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlContext",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(contextDef);
var contextClass = XamlIlContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
xamlLanguage);
var compiler = new AvaloniaXamlIlCompiler(compilerConfig, contextClass);
var editorBrowsableAttribute = typeSystem
.GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute"))
.Resolve();
var editorBrowsableCtor =
asm.MainModule.ImportReference(editorBrowsableAttribute.GetConstructors()
.First(c => c.Parameters.Count == 1));
var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers");
var rootServiceProviderField = asm.MainModule.ImportReference(
typeSystem.GetTypeReference(runtimeHelpers).Resolve().Fields
.First(x => x.Name == "RootServiceProviderV1"));
var loaderDispatcherDef = new TypeDefinition("CompiledAvaloniaXaml", "!XamlLoader",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
loaderDispatcherDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor)
{
ConstructorArguments = {new CustomAttributeArgument(editorBrowsableCtor.Parameters[0].ParameterType, 1)}
});
var loaderDispatcherMethod = new MethodDefinition("TryLoad",
MethodAttributes.Static | MethodAttributes.Public,
asm.MainModule.TypeSystem.Object)
{
Parameters = {new ParameterDefinition(asm.MainModule.TypeSystem.String)}
};
loaderDispatcherDef.Methods.Add(loaderDispatcherMethod);
asm.MainModule.Types.Add(loaderDispatcherDef);
var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First(
m =>
m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 2 &&
m.ReturnType.FullName == "System.Boolean"
&& m.Parameters[0].ParameterType.FullName == "System.String"
&& m.Parameters[1].ParameterType.FullName == "System.String"));
bool CompileGroup(IResourceGroup group)
{
var typeDef = new TypeDefinition("CompiledAvaloniaXaml", "!"+ group.Name,
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
typeDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor)
{
ConstructorArguments = {new CustomAttributeArgument(editorBrowsableCtor.Parameters[0].ParameterType, 1)}
});
asm.MainModule.Types.Add(typeDef);
var builder = typeSystem.CreateTypeBuilder(typeDef);
foreach (var res in group.Resources.Where(CheckXamlName))
{
try
{
// StreamReader is needed here to handle BOM
var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
var parsed = XDocumentXamlIlParser.Parse(xaml);
var initialRoot = (XamlIlAstObjectNode)parsed.Root;
var precompileDirective = initialRoot.Children.OfType<XamlIlAstXmlDirective>()
.FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Precompile");
if (precompileDirective != null)
{
var precompileText = (precompileDirective.Values[0] as XamlIlAstTextNode)?.Text.Trim()
.ToLowerInvariant();
if (precompileText == "false")
continue;
if (precompileText != "true")
throw new XamlIlParseException("Invalid value for x:Precompile", precompileDirective);
}
var classDirective = initialRoot.Children.OfType<XamlIlAstXmlDirective>()
.FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class");
IXamlIlType classType = null;
if (classDirective != null)
{
if (classDirective.Values.Count != 1 || !(classDirective.Values[0] is XamlIlAstTextNode tn))
throw new XamlIlParseException("x:Class should have a string value", classDirective);
classType = typeSystem.TargetAssembly.FindType(tn.Text);
if (classType == null)
throw new XamlIlParseException($"Unable to find type `{tn.Text}`", classDirective);
initialRoot.Type = new XamlIlAstClrTypeReference(classDirective, classType, false);
initialRoot.Children.Remove(classDirective);
}
compiler.Transform(parsed);
var populateName = classType == null ? "Populate:" + res.Name : "!XamlIlPopulate";
var buildName = classType == null ? "Build:" + res.Name : null;
var classTypeDefinition =
classType == null ? null : typeSystem.GetTypeReference(classType).Resolve();
var populateBuilder = classTypeDefinition == null ?
builder :
typeSystem.CreateTypeBuilder(classTypeDefinition);
compiler.Compile(parsed, contextClass,
compiler.DefinePopulateMethod(populateBuilder, parsed, populateName,
classTypeDefinition == null),
buildName == null ? null : compiler.DefineBuildMethod(builder, parsed, buildName, true),
builder.DefineSubType(compilerConfig.WellKnownTypes.Object, "NamespaceInfo:" + res.Name,
true),
(closureName, closureBaseType) =>
populateBuilder.DefineSubType(closureBaseType, closureName, false),
res.Uri, res
);
if (classTypeDefinition != null)
{
var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve()
.Methods.First(m => m.Name == populateName);
var designLoaderFieldType = typeSystem
.GetType("System.Action`1")
.MakeGenericType(typeSystem.GetType("System.Object"));
var designLoaderFieldTypeReference = (GenericInstanceType)typeSystem.GetTypeReference(designLoaderFieldType);
designLoaderFieldTypeReference.GenericArguments[0] =
asm.MainModule.ImportReference(designLoaderFieldTypeReference.GenericArguments[0]);
designLoaderFieldTypeReference = (GenericInstanceType)
asm.MainModule.ImportReference(designLoaderFieldTypeReference);
var designLoaderLoad =
typeSystem.GetMethodReference(
designLoaderFieldType.Methods.First(m => m.Name == "Invoke"));
designLoaderLoad =
asm.MainModule.ImportReference(designLoaderLoad);
designLoaderLoad.DeclaringType = designLoaderFieldTypeReference;
var designLoaderField = new FieldDefinition("!XamlIlPopulateOverride",
FieldAttributes.Static | FieldAttributes.Private, designLoaderFieldTypeReference);
classTypeDefinition.Fields.Add(designLoaderField);
const string TrampolineName = "!XamlIlPopulateTrampoline";
var trampoline = new MethodDefinition(TrampolineName,
MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void);
trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition));
classTypeDefinition.Methods.Add(trampoline);
var regularStart = Instruction.Create(OpCodes.Ldsfld, rootServiceProviderField);
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, designLoaderLoad));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
trampoline.Body.Instructions.Add(regularStart);
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod));
trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
CopyDebugDocument(trampoline, compiledPopulateMethod);
var foundXamlLoader = false;
// Find AvaloniaXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this)
foreach (var method in classTypeDefinition.Methods
.Where(m => !m.Attributes.HasFlag(MethodAttributes.Static)))
{
var i = method.Body.Instructions;
for (var c = 1; c < i.Count; c++)
{
if (i[c - 1].OpCode == OpCodes.Ldarg_0
&& i[c].OpCode == OpCodes.Call)
{
var op = i[c].Operand as MethodReference;
// TODO: Throw an error
// This usually happens when same XAML resource was added twice for some weird reason
// We currently support it for dual-named default theme resource
if (op != null
&& op.Name == TrampolineName)
{
foundXamlLoader = true;
break;
}
if (op != null
&& op.Name == "Load"
&& op.Parameters.Count == 1
&& op.Parameters[0].ParameterType.FullName == "System.Object"
&& op.DeclaringType.FullName == "Avalonia.Markup.Xaml.AvaloniaXamlLoader")
{
i[c].Operand = trampoline;
foundXamlLoader = true;
}
}
}
}
if (!foundXamlLoader)
{
var ctors = classTypeDefinition.GetConstructors()
.Where(c => !c.IsStatic).ToList();
// We can inject xaml loader into default constructor
if (ctors.Count == 1 && ctors[0].Body.Instructions.Count(o=>o.OpCode != OpCodes.Nop) == 3)
{
var i = ctors[0].Body.Instructions;
var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret));
i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline));
i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
}
else
{
throw new InvalidProgramException(
$"No call to AvaloniaXamlLoader.Load(this) call found anywhere in the type {classType.FullName} and type seems to have custom constructors.");
}
}
}
if (buildName != null || classTypeDefinition != null)
{
var compiledBuildMethod = buildName == null ?
null :
typeSystem.GetTypeReference(builder).Resolve()
.Methods.First(m => m.Name == buildName);
var parameterlessConstructor = compiledBuildMethod != null ?
null :
classTypeDefinition.GetConstructors().FirstOrDefault(c =>
c.IsPublic && !c.IsStatic && !c.HasParameters);
if (compiledBuildMethod != null || parameterlessConstructor != null)
{
var i = loaderDispatcherMethod.Body.Instructions;
var nop = Instruction.Create(OpCodes.Nop);
i.Add(Instruction.Create(OpCodes.Ldarg_0));
i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri));
i.Add(Instruction.Create(OpCodes.Call, stringEquals));
i.Add(Instruction.Create(OpCodes.Brfalse, nop));
if (parameterlessConstructor != null)
i.Add(Instruction.Create(OpCodes.Newobj, parameterlessConstructor));
else
{
i.Add(Instruction.Create(OpCodes.Ldsfld, rootServiceProviderField));
i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod));
}
i.Add(Instruction.Create(OpCodes.Ret));
i.Add(nop);
}
}
}
catch (Exception e)
{
int lineNumber = 0, linePosition = 0;
if (e is XamlIlParseException xe)
{
lineNumber = xe.LineNumber;
linePosition = xe.LinePosition;
}
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", res.FilePath,
lineNumber, linePosition, lineNumber, linePosition,
e.Message, "", "Avalonia"));
return false;
}
res.Remove();
}
return true;
}
if (emres.Resources.Count(CheckXamlName) != 0)
if (!CompileGroup(emres))
return new CompileResult(false);
if (avares.Resources.Count(CheckXamlName) != 0)
{
if (!CompileGroup(avares))
return new CompileResult(false);
avares.Save();
}
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull));
loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
asm.Write(output, new WriterParameters
{
WriteSymbols = asm.MainModule.HasSymbols
});
return new CompileResult(true, true);
}
}
}

6
src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
@ -11,10 +11,14 @@
<ProjectReference Include="..\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<!-- Compatibility with old apps -->
<EmbeddedResource Include="Themes\**\*.xaml"/>
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\JetBrains.Annotations.props" />
<Import Project="..\..\build\BuildTargets.targets" />
</Project>

1
src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs

@ -26,6 +26,7 @@ namespace Avalonia.Controls
/// Gets or sets the binding that associates the column with a property in the data source.
/// </summary>
//TODO Binding
[AssignBinding]
public virtual IBinding Binding
{
get

50
src/Avalonia.Controls/LayoutTransformControl.cs

@ -20,6 +20,9 @@ namespace Avalonia.Controls
public static readonly AvaloniaProperty<Transform> LayoutTransformProperty =
AvaloniaProperty.Register<LayoutTransformControl, Transform>(nameof(LayoutTransform));
public static readonly AvaloniaProperty<bool> UseRenderTransformProperty =
AvaloniaProperty.Register<LayoutTransformControl, bool>(nameof(LayoutTransform));
static LayoutTransformControl()
{
ClipToBoundsProperty.OverrideDefaultValue<LayoutTransformControl>(true);
@ -29,6 +32,7 @@ namespace Avalonia.Controls
ChildProperty.Changed
.AddClassHandler<LayoutTransformControl>(x => x.OnChildChanged);
UseRenderTransformProperty.Changed.AddClassHandler<LayoutTransformControl>(x => x.OnUseRenderTransformPropertyChanged);
}
/// <summary>
@ -40,6 +44,15 @@ namespace Avalonia.Controls
set { SetValue(LayoutTransformProperty, value); }
}
/// <summary>
/// Utilize the <see cref="RenderTransformProperty"/> for layout transforms.
/// </summary>
public bool UseRenderTransform
{
get { return GetValue(UseRenderTransformProperty); }
set { SetValue(UseRenderTransformProperty, value); }
}
public IControl TransformRoot => Child;
/// <summary>
@ -51,6 +64,7 @@ namespace Avalonia.Controls
{
if (TransformRoot == null || LayoutTransform == null)
{
LayoutTransform = RenderTransform;
return base.ArrangeOverride(finalSize);
}
@ -133,6 +147,42 @@ namespace Avalonia.Controls
return transformedDesiredSize;
}
IDisposable _renderTransformChangedEvent;
private void OnUseRenderTransformPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
// HACK: In theory, this method and the UseRenderTransform shouldn't exist but
// it's hard to animate this particular control with style animations without
// PropertyPaths.
//
// So until we get that implemented, we'll stick on this not-so-good
// workaround.
var target = e.Sender as LayoutTransformControl;
var shouldUseRenderTransform = (bool)e.NewValue;
if (target != null)
{
if (shouldUseRenderTransform)
{
_renderTransformChangedEvent = RenderTransformProperty.Changed
.Subscribe(
(x) =>
{
var target2 = x.Sender as LayoutTransformControl;
if (target2 != null)
{
target2.LayoutTransform = target2.RenderTransform;
}
});
}
else
{
_renderTransformChangedEvent?.Dispose();
LayoutTransform = null;
}
}
}
private void OnChildChanged(AvaloniaPropertyChangedEventArgs e)
{
if (null != TransformRoot)

23
src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs

@ -0,0 +1,23 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// Represents a notification manager that can show arbitrary content.
/// Managed notification managers can show any content.
/// </summary>
/// <remarks>
/// Because notification managers of this type are implemented purely in managed code, they
/// can display arbitrary content, as opposed to notification managers which display notifications
/// using the host operating system's notification mechanism.
/// </remarks>
public interface IManagedNotificationManager : INotificationManager
{
/// <summary>
/// Shows a notification.
/// </summary>
/// <param name="content">The content to be displayed.</param>
void Show(object content);
}
}

44
src/Avalonia.Controls/Notifications/INotification.cs

@ -0,0 +1,44 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// Represents a notification that can be shown in a window or by the host operating system.
/// </summary>
public interface INotification
{
/// <summary>
/// Gets the Title of the notification.
/// </summary>
string Title { get; }
/// <summary>
/// Gets the notification message.
/// </summary>
string Message { get; }
/// <summary>
/// Gets the <see cref="NotificationType"/> of the notification.
/// </summary>
NotificationType Type { get; }
/// <summary>
/// Gets the expiration time of the notification after which it will automatically close.
/// If the value is <see cref="TimeSpan.Zero"/> then the notification will remain open until the user closes it.
/// </summary>
TimeSpan Expiration { get; }
/// <summary>
/// Gets an Action to be run when the notification is clicked.
/// </summary>
Action OnClick { get; }
/// <summary>
/// Gets an Action to be run when the notification is closed.
/// </summary>
Action OnClose { get; }
}
}

18
src/Avalonia.Controls/Notifications/INotificationManager.cs

@ -0,0 +1,18 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// Represents a notification manager that can be used to show notifications in a window or using
/// the host operating system.
/// </summary>
public interface INotificationManager
{
/// <summary>
/// Show a notification.
/// </summary>
/// <param name="notification">The notification to be displayed.</param>
void Show(INotification notification);
}
}

60
src/Avalonia.Controls/Notifications/Notification.cs

@ -0,0 +1,60 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// A notification that can be shown in a window or by the host operating system.
/// </summary>
/// <remarks>
/// This class represents a notification that can be displayed either in a window using
/// <see cref="WindowNotificationManager"/> or by the host operating system (to be implemented).
/// </remarks>
public class Notification : INotification
{
/// <summary>
/// Initializes a new instance of the <see cref="Notification"/> class.
/// </summary>
/// <param name="title">The title of the notification.</param>
/// <param name="message">The message to be displayed in the notification.</param>
/// <param name="type">The <see cref="NotificationType"/> of the notification.</param>
/// <param name="expiration">The expiry time at which the notification will close.
/// Use <see cref="TimeSpan.Zero"/> for notifications that will remain open.</param>
/// <param name="onClick">An Action to call when the notification is clicked.</param>
/// <param name="onClose">An Action to call when the notification is closed.</param>
public Notification(string title,
string message,
NotificationType type = NotificationType.Information,
TimeSpan? expiration = null,
Action onClick = null,
Action onClose = null)
{
Title = title;
Message = message;
Type = type;
Expiration = expiration.HasValue ? expiration.Value : TimeSpan.FromSeconds(5);
OnClick = onClick;
OnClose = onClose;
}
/// <inheritdoc/>
public string Title { get; private set; }
/// <inheritdoc/>
public string Message { get; private set; }
/// <inheritdoc/>
public NotificationType Type { get; private set; }
/// <inheritdoc/>
public TimeSpan Expiration { get; private set; }
/// <inheritdoc/>
public Action OnClick { get; private set; }
/// <inheritdoc/>
public Action OnClose { get; private set; }
}
}

160
src/Avalonia.Controls/Notifications/NotificationCard.cs

@ -0,0 +1,160 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// Control that represents and displays a notification.
/// </summary>
public class NotificationCard : ContentControl
{
private bool _isClosed;
private bool _isClosing;
/// <summary>
/// Initializes a new instance of the <see cref="NotificationCard"/> class.
/// </summary>
public NotificationCard()
{
this.GetObservable(IsClosedProperty)
.Subscribe(x =>
{
if (!IsClosing && !IsClosed)
{
return;
}
RaiseEvent(new RoutedEventArgs(NotificationClosedEvent));
});
this.GetObservable(ContentProperty)
.OfType<Notification>()
.Subscribe(x =>
{
switch (x.Type)
{
case NotificationType.Error:
PseudoClasses.Add(":error");
break;
case NotificationType.Information:
PseudoClasses.Add(":information");
break;
case NotificationType.Success:
PseudoClasses.Add(":success");
break;
case NotificationType.Warning:
PseudoClasses.Add(":warning");
break;
}
});
}
/// <summary>
/// Determines if the notification is already closing.
/// </summary>
public bool IsClosing
{
get { return _isClosing; }
private set { SetAndRaise(IsClosingProperty, ref _isClosing, value); }
}
/// <summary>
/// Defines the <see cref="IsClosing"/> property.
/// </summary>
public static readonly DirectProperty<NotificationCard, bool> IsClosingProperty =
AvaloniaProperty.RegisterDirect<NotificationCard, bool>(nameof(IsClosing), o => o.IsClosing);
/// <summary>
/// Determines if the notification is closed.
/// </summary>
public bool IsClosed
{
get { return _isClosed; }
set { SetAndRaise(IsClosedProperty, ref _isClosed, value); }
}
/// <summary>
/// Defines the <see cref="IsClosed"/> property.
/// </summary>
public static readonly DirectProperty<NotificationCard, bool> IsClosedProperty =
AvaloniaProperty.RegisterDirect<NotificationCard, bool>(nameof(IsClosed), o => o.IsClosed, (o, v) => o.IsClosed = v);
/// <summary>
/// Defines the <see cref="NotificationClosed"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> NotificationClosedEvent =
RoutedEvent.Register<NotificationCard, RoutedEventArgs>(nameof(NotificationClosed), RoutingStrategies.Bubble);
/// <summary>
/// Raised when the <see cref="NotificationCard"/> has closed.
/// </summary>
public event EventHandler<RoutedEventArgs> NotificationClosed
{
add { AddHandler(NotificationClosedEvent, value); }
remove { RemoveHandler(NotificationClosedEvent, value); }
}
public static bool GetCloseOnClick(Button obj)
{
return (bool)obj.GetValue(CloseOnClickProperty);
}
public static void SetCloseOnClick(Button obj, bool value)
{
obj.SetValue(CloseOnClickProperty, value);
}
/// <summary>
/// Defines the CloseOnClick property.
/// </summary>
public static readonly AvaloniaProperty CloseOnClickProperty =
AvaloniaProperty.RegisterAttached<Button, bool>("CloseOnClick", typeof(NotificationCard), validate: CloseOnClickChanged);
private static bool CloseOnClickChanged(Button button, bool value)
{
if (value)
{
button.Click += Button_Click;
}
else
{
button.Click -= Button_Click;
}
return true;
}
/// <summary>
/// Called when a button inside the Notification is clicked.
/// </summary>
private static void Button_Click(object sender, RoutedEventArgs e)
{
var btn = sender as ILogical;
var notification = btn.GetLogicalAncestors().OfType<NotificationCard>().FirstOrDefault();
notification?.Close();
}
/// <summary>
/// Closes the <see cref="NotificationCard"/>.
/// </summary>
public void Close()
{
if (IsClosing)
{
return;
}
IsClosing = true;
}
}
}

17
src/Avalonia.Controls/Notifications/NotificationPosition.cs

@ -0,0 +1,17 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// Describes the possible positions for <see cref="Notification"/>s that are displayed by a
/// <see cref="WindowNotificationManager"/>.
/// </summary>
public enum NotificationPosition
{
TopLeft,
TopRight,
BottomLeft,
BottomRight
}
}

16
src/Avalonia.Controls/Notifications/NotificationType.cs

@ -0,0 +1,16 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// Enumeration of types for <see cref="INotification"/>.
/// </summary>
public enum NotificationType
{
Information,
Success,
Warning,
Error
}
}

64
src/Avalonia.Controls/Notifications/ReversibleStackPanel.cs

@ -0,0 +1,64 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using Avalonia.Layout;
namespace Avalonia.Controls
{
/// <summary>
/// Implements a <see cref="StackPanel"/> where the flow direction of its items can be reversed.
/// </summary>
public class ReversibleStackPanel : StackPanel
{
/// <summary>
/// Defines the <see cref="ReverseOrder"/> property.
/// </summary>
public static readonly StyledProperty<bool> ReverseOrderProperty =
AvaloniaProperty.Register<ReversibleStackPanel, bool>(nameof(ReverseOrder));
/// <summary>
/// Gets or sets if the child controls will be layed out in reverse order.
/// </summary>
public bool ReverseOrder
{
get => GetValue(ReverseOrderProperty);
set => SetValue(ReverseOrderProperty, value);
}
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
var orientation = Orientation;
var spacing = Spacing;
var finalRect = new Rect(finalSize);
var pos = 0.0;
var children = ReverseOrder ? Children.Reverse() : Children;
foreach (Control child in children)
{
double childWidth = child.DesiredSize.Width;
double childHeight = child.DesiredSize.Height;
if (orientation == Orientation.Vertical)
{
var rect = new Rect(0, pos, childWidth, childHeight)
.Align(finalRect, child.HorizontalAlignment, VerticalAlignment.Top);
ArrangeChild(child, rect, finalSize, orientation);
pos += childHeight + spacing;
}
else
{
var rect = new Rect(pos, 0, childWidth, childHeight)
.Align(finalRect, HorizontalAlignment.Left, child.VerticalAlignment);
ArrangeChild(child, rect, finalSize, orientation);
pos += childWidth + spacing;
}
}
return finalSize;
}
}
}

163
src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@ -0,0 +1,163 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Primitives;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// An <see cref="INotificationManager"/> that displays notifications in a <see cref="Window"/>.
/// </summary>
public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager
{
private IList _items;
/// <summary>
/// Defines the <see cref="Position"/> property.
/// </summary>
public static readonly StyledProperty<NotificationPosition> PositionProperty =
AvaloniaProperty.Register<WindowNotificationManager, NotificationPosition>(nameof(Position), NotificationPosition.TopRight);
/// <summary>
/// Defines which corner of the screen notifications can be displayed in.
/// </summary>
/// <seealso cref="NotificationPosition"/>
public NotificationPosition Position
{
get { return GetValue(PositionProperty); }
set { SetValue(PositionProperty, value); }
}
/// <summary>
/// Defines the <see cref="MaxItems"/> property.
/// </summary>
public static readonly StyledProperty<int> MaxItemsProperty =
AvaloniaProperty.Register<WindowNotificationManager, int>(nameof(MaxItems), 5);
/// <summary>
/// Defines the maximum number of notifications visible at once.
/// </summary>
public int MaxItems
{
get { return GetValue(MaxItemsProperty); }
set { SetValue(MaxItemsProperty, value); }
}
/// <summary>
/// Initializes a new instance of the <see cref="WindowNotificationManager"/> class.
/// </summary>
/// <param name="host">The window that will host the control.</param>
public WindowNotificationManager(Window host)
{
if (VisualChildren.Count != 0)
{
Install(host);
}
else
{
Observable.FromEventPattern<TemplateAppliedEventArgs>(host, nameof(host.TemplateApplied)).Take(1)
.Subscribe(_ =>
{
Install(host);
});
}
}
static WindowNotificationManager()
{
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopLeft, ":topleft");
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopRight, ":topright");
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomLeft, ":bottomleft");
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomRight, ":bottomright");
HorizontalAlignmentProperty.OverrideDefaultValue<WindowNotificationManager>(Layout.HorizontalAlignment.Stretch);
VerticalAlignmentProperty.OverrideDefaultValue<WindowNotificationManager>(Layout.VerticalAlignment.Stretch);
}
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
var itemsControl = e.NameScope.Find<Panel>("PART_Items");
_items = itemsControl?.Children;
}
/// <inheritdoc/>
public void Show(INotification content)
{
Show(content as object);
}
/// <inheritdoc/>
public async void Show(object content)
{
var notification = content as INotification;
var notificationControl = new NotificationCard
{
Content = content
};
if (notification != null)
{
notificationControl.NotificationClosed += (sender, args) =>
{
notification.OnClose?.Invoke();
_items.Remove(sender);
};
}
notificationControl.PointerPressed += (sender, args) =>
{
if (notification != null && notification.OnClick != null)
{
notification.OnClick.Invoke();
}
(sender as NotificationCard)?.Close();
};
_items.Add(notificationControl);
if (_items.OfType<NotificationCard>().Count(i => !i.IsClosing) > MaxItems)
{
_items.OfType<NotificationCard>().First(i => !i.IsClosing).Close();
}
if (notification != null && notification.Expiration == TimeSpan.Zero)
{
return;
}
await Task.Delay(notification?.Expiration ?? TimeSpan.FromSeconds(5));
notificationControl.Close();
}
/// <summary>
/// Installs the <see cref="WindowNotificationManager"/> within the <see cref="AdornerLayer"/>
/// of the host <see cref="Window"/>.
/// </summary>
/// <param name="host">The <see cref="Window"/> that will be the host.</param>
private void Install(Window host)
{
var adornerLayer = host.GetVisualDescendants()
.OfType<AdornerDecorator>()
.FirstOrDefault()
?.AdornerLayer;
if (adornerLayer != null)
{
adornerLayer.Children.Add(this);
}
}
}
}

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

@ -210,7 +210,7 @@ namespace Avalonia.Controls.Presenters
{
var container = ItemContainerGenerator.ContainerFromIndex(index);
if (container == null)
if (container == null && IsVirtualized)
{
var item = Items.Cast<object>().ElementAt(index);
var materialized = ItemContainerGenerator.Materialize(index, item, MemberSelector);

9
src/Avalonia.Controls/Primitives/AdornerDecorator.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.LogicalTree;
namespace Avalonia.Controls.Primitives
{
public class AdornerDecorator : Decorator
@ -13,6 +15,13 @@ namespace Avalonia.Controls.Primitives
VisualChildren.Add(AdornerLayer);
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
((ILogical)AdornerLayer).NotifyAttachedToLogicalTree(e);
}
public AdornerLayer AdornerLayer
{
get;

6
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -4,14 +4,14 @@
using System;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.VisualTree;
using Avalonia.Media;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives
{
// TODO: Need to track position of adorned elements and move the adorner if they move.
public class AdornerLayer : Panel, ICustomSimpleHitTest
public class AdornerLayer : Canvas, ICustomSimpleHitTest
{
public static readonly AttachedProperty<Visual> AdornedElementProperty =
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, Visual>("AdornedElement");
@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives
}
else
{
child.Arrange(new Rect(child.DesiredSize));
child.Arrange(new Rect(finalSize));
}
}

3
src/Avalonia.Controls/Properties/AssemblyInfo.cs

@ -14,4 +14,5 @@ using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Presenters")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Shapes")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Templates")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Templates")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Notifications")]

5
src/Avalonia.Controls/StackPanel.cs

@ -233,6 +233,11 @@ namespace Avalonia.Controls
foreach (Control child in Children)
{
if (!child.IsVisible)
{
continue;
}
double childWidth = child.DesiredSize.Width;
double childHeight = child.DesiredSize.Height;

3
src/Avalonia.Controls/TopLevel.cs

@ -2,7 +2,9 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Input.Raw;
@ -87,6 +89,7 @@ namespace Avalonia.Controls
}
PlatformImpl = impl;
dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current;
var styler = TryGetService<IStyler>(dependencyResolver);

8
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Xml;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Remote.Protocol;
@ -206,7 +207,8 @@ namespace Avalonia.DesignerSupport.Remote
catch (Exception e)
{
var xamlException = e as XamlException;
var xmlException = e as XmlException;
s_transport.Send(new UpdateXamlResultMessage
{
Error = e.ToString(),
@ -214,8 +216,8 @@ namespace Avalonia.DesignerSupport.Remote
{
ExceptionType = e.GetType().FullName,
Message = e.Message.ToString(),
LineNumber = xamlException?.LineNumber,
LinePosition = xamlException?.LinePosition,
LineNumber = xamlException?.LineNumber ?? xmlException?.LineNumber,
LinePosition = xamlException?.LinePosition ?? xmlException?.LinePosition,
}
});
}

1
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -17,4 +17,5 @@
</ItemGroup>
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\BuildTargets.targets" />
</Project>

4
src/Avalonia.Diagnostics/DevTools.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Avalonia.Diagnostics.DevTools">
<Grid RowDefinitions="Auto,*,Auto">
<TabStrip SelectedIndex="{Binding SelectedTab, Mode=TwoWay}">
<TabStripItem Content="Logical Tree"/>

6
src/Avalonia.Diagnostics/DevTools.xaml.cs

@ -43,6 +43,12 @@ namespace Avalonia.Diagnostics
.Subscribe(RawKeyDown);
}
// HACK: needed for XAMLIL, will fix that later
public DevTools()
{
}
public IControl Root { get; }
public static IDisposable Attach(TopLevel control)

3
src/Avalonia.Diagnostics/Views/EventsView.xaml

@ -1,6 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels">
xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels"
x:Class="Avalonia.Diagnostics.Views.EventsView">
<UserControl.Resources>
<vm:BoolToBrushConverter x:Key="boolToBrush" />
</UserControl.Resources>

4
src/Avalonia.Diagnostics/Views/TreePageView.xaml

@ -1,5 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels">
xmlns:vm="clr-namespace:Avalonia.Diagnostics.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Avalonia.Diagnostics.Views.TreePageView">
<Grid ColumnDefinitions="*,4,3*">
<TreeView Name="tree" Items="{Binding Nodes}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
<TreeView.DataTemplates>

3
src/Avalonia.Styling/Controls/NameScopeExtensions.cs

@ -71,10 +71,11 @@ namespace Avalonia.Controls
{
Contract.Requires<ArgumentNullException>(control != null);
return control.GetSelfAndLogicalAncestors()
var scope = control.GetSelfAndLogicalAncestors()
.OfType<StyledElement>()
.Select(x => (x as INameScope) ?? NameScope.GetNameScope(x))
.FirstOrDefault(x => x != null);
return scope;
}
}
}

11
src/Avalonia.Styling/Styling/Selectors.cs

@ -114,6 +114,17 @@ namespace Avalonia.Styling
{
return new NotSelector(previous, argument(null));
}
/// <summary>
/// Returns a selector which inverts the results of selector argument.
/// </summary>
/// <param name="previous">The previous selector.</param>
/// <param name="argument">The selector to be not-ed.</param>
/// <returns>The selector.</returns>
public static Selector Not(this Selector previous, Selector argument)
{
return new NotSelector(previous, argument);
}
/// <summary>
/// Returns a selector which matches a type.

8
src/Avalonia.Themes.Default/Accents/BaseDark.xaml

@ -1,6 +1,6 @@
<Style xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Style.Resources>
<Color x:Key="ThemeAccentColor">#CC119EDA</Color>
@ -46,6 +46,12 @@
<SolidColorBrush x:Key="ErrorBrush" Color="{DynamicResource ErrorColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ErrorLowBrush" Color="{DynamicResource ErrorLowColor}"></SolidColorBrush>
<SolidColorBrush x:Key="NotificationCardBackgroundBrush" Color="#444444"/>
<SolidColorBrush x:Key="NotificationCardInformationBackgroundBrush" Color="Teal"/>
<SolidColorBrush x:Key="NotificationCardSuccessBackgroundBrush" Color="LimeGreen"/>
<SolidColorBrush x:Key="NotificationCardWarningBackgroundBrush" Color="Orange"/>
<SolidColorBrush x:Key="NotificationCardErrorBackgroundBrush" Color="OrangeRed"/>
<Thickness x:Key="ThemeBorderThickness">1,1,1,1</Thickness>
<sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double>

8
src/Avalonia.Themes.Default/Accents/BaseLight.xaml

@ -1,6 +1,6 @@
<Style xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Style.Resources>
<Color x:Key="ThemeAccentColor">#CC119EDA</Color>
@ -46,6 +46,12 @@
<SolidColorBrush x:Key="ErrorBrush" Color="{DynamicResource ErrorColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ErrorLowBrush" Color="{DynamicResource ErrorLowColor}"></SolidColorBrush>
<SolidColorBrush x:Key="NotificationCardBackgroundBrush" Color="#444444"/>
<SolidColorBrush x:Key="NotificationCardInformationBackgroundBrush" Color="Teal"/>
<SolidColorBrush x:Key="NotificationCardSuccessBackgroundBrush" Color="LimeGreen"/>
<SolidColorBrush x:Key="NotificationCardWarningBackgroundBrush" Color="Orange"/>
<SolidColorBrush x:Key="NotificationCardErrorBackgroundBrush" Color="OrangeRed"/>
<Thickness x:Key="ThemeBorderThickness">1</Thickness>
<sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double>

6
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -14,8 +14,10 @@
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<AvaloniaResource Include="DefaultTheme.xaml"/>
<AvaloniaResource Include="Accents/*.xaml"/>
</ItemGroup>
<Import Project="..\..\build\EmbedXaml.props" />
<!-- Compatibility with old apps, probably need to replace with AvaloniaResource -->
<EmbeddedResource Include="**/*.xaml"/>
</ItemGroup>
<Import Project="..\..\build\BuildTargets.targets"/>
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\Rx.props" />
</Project>

9
src/Avalonia.Themes.Default/DatePicker.xaml

@ -7,7 +7,7 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Style Selector="DatePicker">
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
@ -63,11 +63,8 @@
Grid.ColumnSpan="4"
Grid.RowSpan="3"
FontSize="{DynamicResource FontSizeSmall}"
Foreground="{DynamicResource ThemeBorderHighBrush}">
<TextBlock.Text>
<Binding Source="{x:Static sys:DateTime.Today}" Path="Day"/>
</TextBlock.Text>
</TextBlock>
Foreground="{DynamicResource ThemeBorderHighBrush}"
Text="{Binding Source={x:Static sys:DateTime.Today}, Path=Day}"/>
<Ellipse HorizontalAlignment="Center" VerticalAlignment="Center" Fill="{DynamicResource ThemeControlLowBrush}" StrokeThickness="0" Grid.ColumnSpan="4" Width="3" Height="3"/>
</Grid>

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

@ -1,4 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui">
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Avalonia.Themes.Default.DefaultTheme">
<!-- Define ToolTip first so its styles can be overriden by other controls (e.g. TextBox) -->
<StyleInclude Source="resm:Avalonia.Themes.Default.ToolTip.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.DataValidationErrors.xaml?assembly=Avalonia.Themes.Default"/>
@ -46,4 +48,6 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.ButtonSpinner.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.NumericUpDown.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.AutoCompleteBox.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.WindowNotificationManager.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.NotificationCard.xaml?assembly=Avalonia.Themes.Default"/>
</Styles>

7
src/Avalonia.Themes.Default/DefaultTheme.xaml.cs

@ -11,12 +11,5 @@ namespace Avalonia.Themes.Default
/// </summary>
public class DefaultTheme : Styles
{
/// <summary>
/// Initializes a new instance of the <see cref="DefaultTheme"/> class.
/// </summary>
public DefaultTheme()
{
AvaloniaXamlLoader.Load(this);
}
}
}

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

@ -1,5 +1,5 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Style Selector="MenuItem">
<Setter Property="Background" Value="Transparent"/>

88
src/Avalonia.Themes.Default/NotificationCard.xaml

@ -0,0 +1,88 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="NotificationCard">
<Setter Property="UseLayoutRounding" Value="True"/>
<Setter Property="Width" Value="350"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="RenderTransformOrigin" Value="50%,75%"/>
<Setter Property="Template">
<ControlTemplate>
<LayoutTransformControl Name="PART_LayoutTransformControl" UseRenderTransform="True">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="8,8,0,0">
<ContentControl MinHeight="150" Content="{TemplateBinding Content}" />
</Border>
</LayoutTransformControl>
</ControlTemplate>
</Setter>
<Style.Animations>
<Animation Duration="0:0:0.45" Easing="QuadraticEaseIn" FillMode="Forward">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0"/>
<Setter Property="TranslateTransform.Y" Value="20"/>
<Setter Property="ScaleTransform.ScaleX" Value="0.85"/>
<Setter Property="ScaleTransform.ScaleY" Value="0.85"/>
</KeyFrame>
<KeyFrame Cue="30%">
<Setter Property="TranslateTransform.Y" Value="-20"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1"/>
<Setter Property="TranslateTransform.Y" Value="0"/>
<Setter Property="ScaleTransform.ScaleX" Value="1"/>
<Setter Property="ScaleTransform.ScaleY" Value="1"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="NotificationCard[IsClosing=true] /template/ LayoutTransformControl#PART_LayoutTransformControl">
<Setter Property="RenderTransformOrigin" Value="50%,0%"/>
<Style.Animations>
<Animation Duration="0:0:0.75" Easing="QuadraticEaseOut" FillMode="Forward">
<KeyFrame Cue="0%">
<Setter Property="TranslateTransform.X" Value="0"/>
<Setter Property="ScaleTransform.ScaleY" Value="1"/>
</KeyFrame>
<KeyFrame Cue="70%">
<Setter Property="TranslateTransform.X" Value="800"/>
<Setter Property="ScaleTransform.ScaleY" Value="1"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="ScaleTransform.ScaleY" Value="0"/>
<Setter Property="TranslateTransform.X" Value="800"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="NotificationCard[IsClosing=true]">
<Style.Animations>
<Animation Duration="0:0:1.25" Easing="QuadraticEaseOut" FillMode="Forward">
<KeyFrame Cue="100%">
<Setter Property="IsClosed" Value="True"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="NotificationCard">
<Setter Property="Background" Value="{DynamicResource NotificationCardBackgroundBrush}"/>
</Style>
<Style Selector="NotificationCard:information">
<Setter Property="Background" Value="{DynamicResource NotificationCardInformationBackgroundBrush}"/>
</Style>
<Style Selector="NotificationCard:success">
<Setter Property="Background" Value="{DynamicResource NotificationCardSuccessBackgroundBrush}"/>
</Style>
<Style Selector="NotificationCard:warning">
<Setter Property="Background" Value="{DynamicResource NotificationCardWarningBackgroundBrush}"/>
</Style>
<Style Selector="NotificationCard:error">
<Setter Property="Background" Value="{DynamicResource NotificationCardErrorBackgroundBrush}"/>
</Style>
</Styles>

10
src/Avalonia.Themes.Default/TabItem.xaml

@ -24,19 +24,19 @@
<Style Selector="TabItem:disabled">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style>
<Style Selector="TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="TabItem:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
</Style>
<Style Selector="TabItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="TabItem:selected">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush4}"/>
</Style>
<Style Selector="TabItem:selected:focus /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="TabItem:selected:focus">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush3}"/>
</Style>
<Style Selector="TabItem:selected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="TabItem:selected:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush3}"/>
</Style>
<Style Selector="TabItem:selected:focus:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="TabItem:selected:focus:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
</Style>
<Style Selector="TabItem[TabStripPlacement=Right]">

45
src/Avalonia.Themes.Default/WindowNotificationManager.xaml

@ -0,0 +1,45 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="WindowNotificationManager">
<Setter Property="Margin" Value="0 0 8 8"/>
<Setter Property="Template">
<ControlTemplate>
<ReversibleStackPanel Name="PART_Items">
<ReversibleStackPanel.DataTemplates>
<DataTemplate DataType="INotification">
<StackPanel Spacing="8" Margin="12">
<TextBlock Text="{Binding Title}" FontWeight="Medium" />
<TextBlock MaxHeight="80" Text="{Binding Message}" TextWrapping="Wrap" Margin="0,0,12,0"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="x:String">
<TextBlock Text="{Binding }" Margin="12" />
</DataTemplate>
</ReversibleStackPanel.DataTemplates>
</ReversibleStackPanel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="WindowNotificationManager:topleft /template/ ReversibleStackPanel#PART_Items">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
<Style Selector="WindowNotificationManager:topright /template/ ReversibleStackPanel#PART_Items">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
<Style Selector="WindowNotificationManager:bottomleft /template/ ReversibleStackPanel#PART_Items">
<Setter Property="ReverseOrder" Value="True"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
<Style Selector="WindowNotificationManager:bottomright /template/ ReversibleStackPanel#PART_Items">
<Setter Property="ReverseOrder" Value="True"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</Styles>

110
src/Avalonia.Visuals/Media/FontFamily.cs

@ -11,46 +11,49 @@ namespace Avalonia.Media
{
public class FontFamily
{
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class.
/// </summary>
/// <param name="name">The name of the <see cref="FontFamily"/>.</param>
/// <exception cref="T:System.ArgumentNullException">name</exception>
public FontFamily(string name)
/// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
public FontFamily(string name) : this(null, name)
{
Contract.Requires<ArgumentNullException>(name != null);
FamilyNames = new FamilyNameCollection(new[] { name });
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class.
/// </summary>
/// <param name="names">The names of the <see cref="FontFamily"/>.</param>
/// <exception cref="T:System.ArgumentNullException">name</exception>
public FontFamily(IEnumerable<string> names)
/// <param name="baseUri">Specifies the base uri that is used to resolve font family assets.</param>
/// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
/// <exception cref="T:System.ArgumentException">Base uri must be an absolute uri.</exception>
public FontFamily(Uri baseUri, string name)
{
Contract.Requires<ArgumentNullException>(names != null);
if (string.IsNullOrEmpty(name))
{
FamilyNames = new FamilyNameCollection(string.Empty);
FamilyNames = new FamilyNameCollection(names);
}
return;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class.
/// </summary>
/// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
/// <param name="source">The source of font resources.</param>
/// <param name="baseUri"></param>
public FontFamily(string name, Uri source, Uri baseUri = null) : this(name)
{
Key = new FontFamilyKey(source, baseUri);
var fontFamilySegment = GetFontFamilyIdentifier(name);
if (fontFamilySegment.Source != null)
{
if (baseUri != null && !baseUri.IsAbsoluteUri)
{
throw new ArgumentException("Base uri must be an absolute uri.", nameof(baseUri));
}
Key = new FontFamilyKey(fontFamilySegment.Source, baseUri);
}
FamilyNames = new FamilyNameCollection(fontFamilySegment.Name);
}
/// <summary>
/// Represents the default font family
/// </summary>
public static FontFamily Default => new FontFamily(String.Empty);
public static FontFamily Default => new FontFamily(string.Empty);
/// <summary>
/// Represents all font families in the system. This can be an expensive call depending on platform implementation.
@ -88,46 +91,40 @@ namespace Avalonia.Media
/// <param name="s"></param>
public static implicit operator FontFamily(string s)
{
return Parse(s);
return new FontFamily(s);
}
/// <summary>
/// Parses a <see cref="T:Avalonia.Media.FontFamily"/> string.
/// </summary>
/// <param name="s">The <see cref="T:Avalonia.Media.FontFamily"/> string.</param>
/// <param name="baseUri"></param>
/// <returns></returns>
/// <exception cref="ArgumentException">
/// Specified family is not supported.
/// </exception>
public static FontFamily Parse(string s, Uri baseUri = null)
private struct FontFamilyIdentifier
{
if (string.IsNullOrEmpty(s))
public FontFamilyIdentifier(string name, Uri source)
{
throw new ArgumentException("Specified family is not supported.");
Name = name;
Source = source;
}
var segments = s.Split('#');
public string Name { get; }
public Uri Source { get; }
}
private static FontFamilyIdentifier GetFontFamilyIdentifier(string name)
{
var segments = name.Split('#');
switch (segments.Length)
{
case 1:
{
var names = segments[0].Split(',')
.Select(x => x.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x));
return new FontFamily(names);
return new FontFamilyIdentifier(segments[0], null);
}
case 2:
{
var uri = segments[0].StartsWith("/")
? new Uri(segments[0], UriKind.Relative)
: new Uri(segments[0], UriKind.RelativeOrAbsolute);
var source = segments[0].StartsWith("/")
? new Uri(segments[0], UriKind.Relative)
: new Uri(segments[0], UriKind.RelativeOrAbsolute);
return uri.IsAbsoluteUri
? new FontFamily(segments[1], uri)
: new FontFamily(segments[1], uri, baseUri);
return new FontFamilyIdentifier(segments[1], source);
}
default:
@ -137,6 +134,25 @@ namespace Avalonia.Media
}
}
/// <summary>
/// Parses a <see cref="T:Avalonia.Media.FontFamily"/> string.
/// </summary>
/// <param name="s">The <see cref="T:Avalonia.Media.FontFamily"/> string.</param>
/// <param name="baseUri">Specifies the base uri that is used to resolve font family assets.</param>
/// <returns></returns>
/// <exception cref="ArgumentException">
/// Specified family is not supported.
/// </exception>
public static FontFamily Parse(string s, Uri baseUri = null)
{
if (string.IsNullOrEmpty(s))
{
throw new ArgumentException("Specified family is not supported.", nameof(s));
}
return new FontFamily(baseUri, s);
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>

35
src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs

@ -4,31 +4,28 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
namespace Avalonia.Media.Fonts
{
using System.Text;
public class FamilyNameCollection : IEnumerable<string>
{
{
/// <summary>
/// Initializes a new instance of the <see cref="FamilyNameCollection"/> class.
/// </summary>
/// <param name="familyNames">The family names.</param>
/// <exception cref="ArgumentException">familyNames</exception>
public FamilyNameCollection(IEnumerable<string> familyNames)
public FamilyNameCollection(string familyNames)
{
Contract.Requires<ArgumentNullException>(familyNames != null);
var names = new List<string>(familyNames);
if (names.Count == 0) throw new ArgumentException($"{nameof(familyNames)} must not be empty.");
if (familyNames == null)
{
throw new ArgumentNullException(nameof(familyNames));
}
Names = new ReadOnlyCollection<string>(names);
Names = familyNames.Split(',').Select(x => x.Trim()).ToArray();
PrimaryFamilyName = Names.First();
PrimaryFamilyName = Names[0];
HasFallbacks = Names.Count > 1;
}
@ -55,7 +52,7 @@ namespace Avalonia.Media.Fonts
/// <value>
/// The names.
/// </value>
internal ReadOnlyCollection<string> Names { get; }
internal IReadOnlyList<string> Names { get; }
/// <inheritdoc />
/// <summary>
@ -95,7 +92,10 @@ namespace Avalonia.Media.Fonts
{
builder.Append(Names[index]);
if (index == Names.Count - 1) break;
if (index == Names.Count - 1)
{
break;
}
builder.Append(", ");
}
@ -123,9 +123,12 @@ namespace Avalonia.Media.Fonts
/// </returns>
public override bool Equals(object obj)
{
if (!(obj is FamilyNameCollection other)) return false;
if (!(obj is FamilyNameCollection other))
{
return false;
}
return other.ToString().Equals(ToString());
}
}
}
}

2
src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs

@ -95,7 +95,7 @@ namespace Avalonia.Media.Fonts
{
if (!Source.IsAbsoluteUri && BaseUri != null)
{
return BaseUri.Authority + Source;
return BaseUri.AbsoluteUri + Source.OriginalString;
}
return Source.ToString();

2
src/Avalonia.Visuals/Visual.cs

@ -10,6 +10,7 @@ using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Rendering;
using Avalonia.Utilities;
using Avalonia.VisualTree;
@ -25,6 +26,7 @@ namespace Avalonia
/// <see cref="IRenderer"/> to render the control. To traverse the visual tree, use the
/// extension methods defined in <see cref="VisualExtensions"/>.
/// </remarks>
[UsableDuringInitialization]
public class Visual : StyledElement, IVisual
{
/// <summary>

6
src/Avalonia.X11/XIStructs.cs

@ -197,7 +197,7 @@ namespace Avalonia.X11
unsafe struct XIDeviceChangedEvent
{
public int Type; /* GenericEvent */
public ulong Serial; /* # of last request processed by server */
public UIntPtr Serial; /* # of last request processed by server */
public Bool SendEvent; /* true if this came from a SendEvent request */
public IntPtr Display; /* Display the event was read from */
public int Extension; /* XI extension offset */
@ -214,7 +214,7 @@ namespace Avalonia.X11
struct XIDeviceEvent
{
public XEventName type; /* GenericEvent */
public ulong serial; /* # of last request processed by server */
public UIntPtr serial; /* # of last request processed by server */
public Bool send_event; /* true if this came from a SendEvent request */
public IntPtr display; /* Display the event was read from */
public int extension; /* XI extension offset */
@ -241,7 +241,7 @@ namespace Avalonia.X11
unsafe struct XIEvent
{
public int type; /* GenericEvent */
public ulong serial; /* # of last request processed by server */
public UIntPtr serial; /* # of last request processed by server */
public Bool send_event; /* true if this came from a SendEvent request */
public IntPtr display; /* Display the event was read from */
public int extension; /* XI extension offset */

32
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<DefineConstants>PCL;NETSTANDARD;NETSTANDARD2_0;HAS_TYPE_CONVERTER;HAS_CUSTOM_ATTRIBUTE_PROVIDER</DefineConstants>
<DefineConstants>PCL;NETSTANDARD;NETSTANDARD2_0;HAS_TYPE_CONVERTER;HAS_CUSTOM_ATTRIBUTE_PROVIDER;XAMLIL_INTERNAL</DefineConstants>
<UseCecil>false</UseCecil>
<DefineConstants Condition="$(UseCecil) == true">$(DefineConstants);RUNTIME_XAML_CECIL</DefineConstants>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<EnableDefaultItems>false</EnableDefaultItems>
<NoWarn>CS1591</NoWarn>
@ -16,6 +18,7 @@
<Compile Include="Converters\ParseTypeConverter.cs" />
<Compile Include="Converters\SetterValueTypeConverter.cs" />
<Compile Include="Converters\TimeSpanTypeConverter.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="MarkupExtensions\DynamicResourceExtension.cs" />
<Compile Include="MarkupExtensions\ResourceInclude.cs" />
<Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
@ -51,8 +54,33 @@
<Compile Include="Templates\TemplateContent.cs" />
<Compile Include="Templates\TemplateLoader.cs" />
<Compile Include="Templates\TreeDataTemplate.cs" />
<Compile Include="XamlIl\AvaloniaXamlIlRuntimeCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaBindingExtensionHackTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlAvaloniaPropertyResolver.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlConstructorServiceProviderTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlDesignPropertiesTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlMetadataRemover.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransformInstanceAttachedProperties.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlTransitionsTypeMetadataTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlWellKnownTypes.cs" />
<Compile Include="XamlIl\CompilerExtensions\XamlIlAvaloniaPropertyHelper.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlCompiler.cs" />
<Compile Include="XamlIl\CompilerExtensions\AvaloniaXamlIlLanguage.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AddNameScopeRegistration.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlSetterTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\IgnoredDirectivesTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\AvaloniaXamlIlSelectorTransformer.cs" />
<Compile Include="XamlIl\CompilerExtensions\Transformers\XNameTransformer.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlParentStackProvider.cs" />
<Compile Include="XamlIl\Runtime\IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs" />
<Compile Include="XamlIl\Runtime\XamlIlRuntimeHelpers.cs" />
<Compile Include="XamlLoadException.cs" />
<Compile Include="PortableXaml\portable.xaml.github\src\Portable.Xaml\**\*.cs" Exclude="PortableXaml\portable.xaml.github\src\Portable.Xaml\Assembly\**\*.cs" />
<Compile Include="XamlIl\xamlil.github\src\XamlIl\**\*.cs" />
<Compile Condition="$(UseCecil) == true" Include="XamlIl\xamlil.github\src\XamlIl.Cecil\**\*.cs" />
<Compile Remove="XamlIl\xamlil.github\**\obj\**\*.cs" />
<Compile Include="..\Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />
@ -64,6 +92,8 @@
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Markup\Avalonia.Markup.csproj" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Condition="$(UseCecil) == true" Include="Mono.Cecil" Version="0.10.3" />
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
</Project>

1
src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs

@ -30,6 +30,7 @@ namespace Avalonia.Markup.Xaml
/// </remarks>
public static class AvaloniaTypeConverters
{
// When adding item to that list make sure to modify AvaloniaXamlIlLanguage
private static Dictionary<Type, Type> _converters = new Dictionary<Type, Type>()
{
{ typeof(AvaloniaList<>), typeof(AvaloniaListConverter<>) },

35
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -10,6 +10,7 @@ using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Xml.Linq;
using Avalonia.Markup.Xaml.XamlIl;
using Avalonia.Controls;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.PortableXaml;
@ -25,6 +26,8 @@ namespace Avalonia.Markup.Xaml
{
public bool IsDesignMode { get; set; }
public static bool UseLegacyXamlLoader { get; set; } = false;
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
/// </summary>
@ -65,8 +68,8 @@ namespace Avalonia.Markup.Xaml
{
throw new InvalidOperationException(
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
}
foreach (var uri in GetUrisFor(assetLocator, type))
{
if (assetLocator.Exists(uri))
@ -113,21 +116,27 @@ namespace Avalonia.Markup.Xaml
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
var compiledLoader = assetLocator.GetAssembly(uri, baseUri)
?.GetType("CompiledAvaloniaXaml.!XamlLoader")
?.GetMethod("TryLoad", new[] {typeof(string)});
if (compiledLoader != null)
{
var uriString = (!uri.IsAbsoluteUri && baseUri != null ? new Uri(baseUri, uri) : uri)
.ToString();
var compiledResult = compiledLoader.Invoke(null, new object[] {uriString});
if (compiledResult != null)
return compiledResult;
}
var asset = assetLocator.OpenAndGetAssembly(uri, baseUri);
using (var stream = asset.stream)
{
var absoluteUri = uri.IsAbsoluteUri ? uri : new Uri(baseUri, uri);
try
{
return Load(stream, asset.assembly, rootInstance, absoluteUri);
}
catch (Exception e)
{
throw new XamlLoadException("Error loading xaml at " + absoluteUri + ": " + e.Message, e);
}
return Load(stream, asset.assembly, rootInstance, absoluteUri);
}
}
/// <summary>
/// Loads XAML from a string.
/// </summary>
@ -159,6 +168,10 @@ namespace Avalonia.Markup.Xaml
/// <returns>The loaded object.</returns>
public object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null)
{
if (!UseLegacyXamlLoader)
return AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, IsDesignMode);
var readerSettings = new XamlXmlReaderSettings()
{
BaseUri = uri,

4
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

@ -28,8 +28,8 @@ namespace Avalonia.Markup.Xaml.Converters
var parser = new PropertyParser();
var (ns, owner, propertyName) = parser.Parse(new CharacterReader(((string)value).AsSpan()));
var ownerType = TryResolveOwnerByName(context, ns, owner);
var targetType = context.GetFirstAmbientValue<ControlTemplate>()?.TargetType ??
context.GetFirstAmbientValue<Style>()?.Selector?.TargetType ??
var targetType = context.GetFirstParent<ControlTemplate>()?.TargetType ??
context.GetFirstParent<Style>()?.Selector?.TargetType ??
typeof(Control);
var effectiveOwner = ownerType ?? targetType;
var property = registry.FindRegistered(effectiveOwner, propertyName);

7
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs

@ -17,9 +17,10 @@ namespace Avalonia.Markup.Xaml.Converters
if (s == null)
return null;
//On Unix Uri tries to interpret paths starting with "/" as file Uris
if (s.StartsWith("/"))
return new Uri(s, UriKind.Relative);
return new Uri(s);
var kind = s.StartsWith("/") ? UriKind.Relative : UriKind.Absolute;
if (!Uri.TryCreate(s, kind, out var res))
throw new ArgumentException("Unable to parse URI: " + s);
return res;
}
}
}

4
src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs

@ -29,7 +29,7 @@ namespace Avalonia.Markup.Xaml.Converters
return new Bitmap(uri.LocalPath);
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new Bitmap(assets.Open(uri, context.GetBaseUri()));
return new Bitmap(assets.Open(uri, context.GetContextBaseUri()));
}
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs

@ -22,7 +22,7 @@ namespace Avalonia.Markup.Xaml.Converters
{
var s = (string)value;
return FontFamily.Parse(s, context.GetBaseUri());
return FontFamily.Parse(s, context.GetContextBaseUri());
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs

@ -45,7 +45,7 @@ namespace Avalonia.Markup.Xaml.Converters
if(uri.IsAbsoluteUri && uri.IsFile)
return new WindowIcon(uri.LocalPath);
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new WindowIcon(assets.Open(uri, context.GetBaseUri()));
return new WindowIcon(assets.Open(uri, context.GetContextBaseUri()));
}
}
}

61
src/Markup/Avalonia.Markup.Xaml/Extensions.cs

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.Runtime;
using Portable.Xaml;
using Portable.Xaml.Markup;
namespace Avalonia.Markup.Xaml
{
internal static class Extensions
{
public static T GetService<T>(this IServiceProvider sp) => (T)sp?.GetService(typeof(T));
public static Uri GetContextBaseUri(this IServiceProvider ctx)
{
var properService = ctx.GetService<IUriContext>();
if (properService != null)
return properService.BaseUri;
// Ugly hack with casts
return Portable.Xaml.ComponentModel.TypeDescriptorExtensions.GetBaseUri((ITypeDescriptorContext)ctx);
}
public static T GetFirstParent<T>(this IServiceProvider ctx) where T : class
{
var parentStack = ctx.GetService<IAvaloniaXamlIlParentStackProvider>();
if (parentStack != null)
return parentStack.Parents.OfType<T>().FirstOrDefault();
return Portable.Xaml.ComponentModel.TypeDescriptorExtensions.GetFirstAmbientValue<T>((ITypeDescriptorContext)ctx);
}
public static T GetLastParent<T>(this IServiceProvider ctx) where T : class
{
var parentStack = ctx.GetService<IAvaloniaXamlIlParentStackProvider>();
if (parentStack != null)
return parentStack.Parents.OfType<T>().LastOrDefault();
return Portable.Xaml.ComponentModel.TypeDescriptorExtensions.GetLastOrDefaultAmbientValue<T>(
(ITypeDescriptorContext)ctx);
}
public static IEnumerable<T> GetParents<T>(this IServiceProvider sp)
{
var stack = sp.GetService<IAvaloniaXamlIlParentStackProvider>();
if (stack != null)
return stack.Parents.OfType<T>();
var context = (ITypeDescriptorContext)sp;
var schemaContext = context.GetService<IXamlSchemaContextProvider>().SchemaContext;
var ambientProvider = context.GetService<IAmbientProvider>();
return ambientProvider.GetAllAmbientValues(schemaContext.GetXamlType(typeof(T))).OfType<T>();
}
public static Type ResolveType(this IServiceProvider ctx, string namespacePrefix, string type)
{
var tr = ctx.GetService<IXamlTypeResolver>();
string name = string.IsNullOrEmpty(namespacePrefix) ? type : $"{namespacePrefix}:{type}";
return tr?.Resolve(name);
}
}
}

11
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -29,6 +29,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return ProvideTypedValue(serviceProvider);
}
public Binding ProvideTypedValue(IServiceProvider serviceProvider)
{
var descriptorContext = (ITypeDescriptorContext)serviceProvider;
@ -49,18 +54,18 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
};
}
private static object GetDefaultAnchor(ITypeDescriptorContext context)
private static object GetDefaultAnchor(IServiceProvider context)
{
// If the target is not a control, so we need to find an anchor that will let us look
// up named controls and style resources. First look for the closest IControl in
// the context.
object anchor = context.GetFirstAmbientValue<IControl>();
object anchor = context.GetFirstParent<IControl>();
// If a control was not found, then try to find the highest-level style as the XAML
// file could be a XAML file containing only styles.
return anchor ??
context.GetService<IRootObjectProvider>()?.RootObject as IStyle ??
context.GetLastOrDefaultAmbientValue<IStyle>();
context.GetLastParent<IStyle>();
}
public IValueConverter Converter { get; set; }

20
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@ -28,14 +28,15 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public string ResourceKey { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
public override object ProvideValue(IServiceProvider serviceProvider) => ProvideTypedValue(serviceProvider);
public IBinding ProvideTypedValue(IServiceProvider serviceProvider)
{
var context = (ITypeDescriptorContext)serviceProvider;
var provideTarget = context.GetService<IProvideValueTarget>();
var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
if (!(provideTarget.TargetObject is IResourceNode))
{
_anchor = GetAnchor<IResourceNode>(context);
_anchor = serviceProvider.GetFirstParent<IResourceNode>();
}
return this;
@ -56,16 +57,5 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
return null;
}
private T GetAnchor<T>(ITypeDescriptorContext context) where T : class
{
var schemaContext = context.GetService<IXamlSchemaContextProvider>().SchemaContext;
var ambientProvider = context.GetService<IAmbientProvider>();
var xamlType = schemaContext.GetXamlType(typeof(T));
// We override XamlType.CanAssignTo in BindingXamlType so the results we get back
// from GetAllAmbientValues aren't necessarily of the correct type.
return ambientProvider.GetAllAmbientValues(xamlType).OfType<T>().FirstOrDefault();
}
}
}

7
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs

@ -54,9 +54,14 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
/// <inhertidoc/>
public override object ProvideValue(IServiceProvider serviceProvider)
{
return ProvideTypedValue(serviceProvider);
}
public ResourceInclude ProvideTypedValue(IServiceProvider serviceProvider)
{
var tdc = (ITypeDescriptorContext)serviceProvider;
_baseUri = tdc?.GetBaseUri();
_baseUri = tdc?.GetContextBaseUri();
return this;
}
}

34
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@ -28,36 +28,32 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public override object ProvideValue(IServiceProvider serviceProvider)
{
var context = (ITypeDescriptorContext)serviceProvider;
var schemaContext = context.GetService<IXamlSchemaContextProvider>().SchemaContext;
var ambientProvider = context.GetService<IAmbientProvider>();
var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceNode));
var ambientValues = ambientProvider.GetAllAmbientValues(resourceProviderType);
// Look upwards though the ambient context for IResourceProviders which might be able
// to give us the resource.
foreach (var ambientValue in ambientValues)
foreach (var resourceProvider in serviceProvider.GetParents<IResourceNode>())
{
// We override XamlType.CanAssignTo in BindingXamlType so the results we get back
// from GetAllAmbientValues aren't necessarily of the correct type.
if (ambientValue is IResourceNode resourceProvider)
if (AvaloniaXamlLoader.UseLegacyXamlLoader
&& resourceProvider is IControl control && control.StylingParent != null)
{
if (resourceProvider is IControl control && control.StylingParent != null)
{
// If we've got to a control that has a StylingParent then it's probably
// a top level control and its StylingParent is pointing to the global
// styles. If this is case just do a FindResource on it.
return control.FindResource(ResourceKey);
}
else if (resourceProvider.TryGetResource(ResourceKey, out var value))
{
return value;
}
// If we've got to a control that has a StylingParent then it's probably
// a top level control and its StylingParent is pointing to the global
// styles. If this is case just do a FindResource on it.
return control.FindResource(ResourceKey);
}
else if (resourceProvider.TryGetResource(ResourceKey, out var value))
{
return value;
}
}
// The resource still hasn't been found, so add a delayed one-time binding.
var provideTarget = context.GetService<IProvideValueTarget>();
var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
if (provideTarget.TargetObject is IControl target &&
provideTarget.TargetProperty is PropertyInfo property)

8
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs

@ -18,13 +18,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
{
}
public override object ProvideValue(IServiceProvider serviceProvider)
public override object ProvideValue(IServiceProvider serviceProvider) => ProvideTypedValue(serviceProvider);
public IStyle ProvideTypedValue(IServiceProvider serviceProvider)
{
var tdc = (ITypeDescriptorContext)serviceProvider;
return new StyleInclude(tdc.GetBaseUri()) { Source = Source };
return new StyleInclude(serviceProvider.GetContextBaseUri()) { Source = Source };
}
public Uri Source { get; set; }
}
}
}

4
src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs

@ -44,7 +44,7 @@ namespace Portable.Xaml.ComponentModel
var amb = ctx.GetService<IAmbientProvider>();
var sc = ctx.GetService<IXamlSchemaContextProvider>().SchemaContext;
// Because GetFirstAmbientValue uses XamlType.CanAssignTo it returns values that
// Because GetFirstParent uses XamlType.CanAssignTo it returns values that
// aren't actually of the correct type. Use GetAllAmbientValues instead.
return amb.GetAllAmbientValues(sc.GetXamlType(typeof(T))).OfType<T>().FirstOrDefault();
}
@ -98,4 +98,4 @@ namespace Portable.Xaml.ComponentModel
public AvaloniaXamlContext Context { get; }
}
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github

@ -1 +1 @@
Subproject commit 7452b23169e4948907fa10e2c115b672897d0e04
Subproject commit ab5526173722b8988bc5ca3c03c8752ce89c0975

5
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@ -25,6 +25,11 @@ namespace Avalonia.Markup.Xaml.Styling
_baseUri = baseUri;
}
public StyleInclude(IServiceProvider serviceProvider)
{
_baseUri = serviceProvider.GetContextBaseUri();
}
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged
{

7
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

@ -1,6 +1,7 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Controls;
using System.Collections.Generic;
@ -37,7 +38,11 @@ namespace Avalonia.Markup.Xaml.Templates
public static IControl Load(object templateContent)
{
if (templateContent is Func<IServiceProvider, object> direct)
{
return (IControl)direct(null);
}
return ((TemplateContent)templateContent).Load();
}
}
}
}

276
src/Markup/Avalonia.Markup.Xaml/XamlIl/AvaloniaXamlIlRuntimeCompiler.cs

@ -0,0 +1,276 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
using Avalonia.Markup.Xaml.XamlIl.Runtime;
using Avalonia.Platform;
using XamlIl.Transform;
using XamlIl.TypeSystem;
#if RUNTIME_XAML_CECIL
using TypeAttributes = Mono.Cecil.TypeAttributes;
using Mono.Cecil;
using XamlIl.Ast;
#endif
namespace Avalonia.Markup.Xaml.XamlIl
{
static class AvaloniaXamlIlRuntimeCompiler
{
#if !RUNTIME_XAML_CECIL
private static SreTypeSystem _sreTypeSystem;
private static ModuleBuilder _sreBuilder;
private static IXamlIlType _sreContextType;
private static XamlIlLanguageTypeMappings _sreMappings;
private static XamlIlXmlnsMappings _sreXmlns;
private static AssemblyBuilder _sreAsm;
private static bool _sreCanSave;
public static void DumpRuntimeCompilationResults()
{
if (_sreBuilder == null)
return;
var saveMethod = _sreAsm.GetType().GetMethods()
.FirstOrDefault(m => m.Name == "Save" && m.GetParameters().Length == 1);
if (saveMethod == null)
return;
try
{
_sreBuilder.CreateGlobalFunctions();
saveMethod.Invoke(_sreAsm, new Object[] {"XamlIlLoader.ildump"});
}
catch
{
//Ignore
}
}
static void InitializeSre()
{
if (_sreTypeSystem == null)
_sreTypeSystem = new SreTypeSystem();
if (_sreBuilder == null)
{
_sreCanSave = !(RuntimeInformation.FrameworkDescription.StartsWith(".NET Core"));
var name = new AssemblyName(Guid.NewGuid().ToString("N"));
if (_sreCanSave)
{
var define = AppDomain.CurrentDomain.GetType().GetMethods()
.FirstOrDefault(m => m.Name == "DefineDynamicAssembly"
&& m.GetParameters().Length == 3 &&
m.GetParameters()[2].ParameterType == typeof(string));
if (define != null)
_sreAsm = (AssemblyBuilder)define.Invoke(AppDomain.CurrentDomain, new object[]
{
name, (AssemblyBuilderAccess)3,
Path.GetDirectoryName(typeof(AvaloniaXamlIlRuntimeCompiler).Assembly.GetModules()[0]
.FullyQualifiedName)
});
else
_sreCanSave = false;
}
if(_sreAsm == null)
_sreAsm = AssemblyBuilder.DefineDynamicAssembly(name,
AssemblyBuilderAccess.RunAndCollect);
_sreBuilder = _sreAsm.DefineDynamicModule("XamlIlLoader.ildump");
}
if (_sreMappings == null)
_sreMappings = AvaloniaXamlIlLanguage.Configure(_sreTypeSystem);
if (_sreXmlns == null)
_sreXmlns = XamlIlXmlnsMappings.Resolve(_sreTypeSystem, _sreMappings);
if (_sreContextType == null)
_sreContextType = XamlIlContextDefinition.GenerateContextClass(
_sreTypeSystem.CreateTypeBuilder(
_sreBuilder.DefineType("XamlIlContext")), _sreTypeSystem, _sreMappings);
}
static object LoadSre(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode)
{
var success = false;
try
{
var rv = LoadSreCore(xaml, localAssembly, rootInstance, uri, isDesignMode);
success = true;
return rv;
}
finally
{
if(!success && _sreCanSave)
DumpRuntimeCompilationResults();
}
}
static object LoadSreCore(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode)
{
InitializeSre();
var asm = localAssembly == null ? null : _sreTypeSystem.GetAssembly(localAssembly);
var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_sreTypeSystem, asm,
_sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter),
_sreContextType);
var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri);
IXamlIlType overrideType = null;
if (rootInstance != null)
{
overrideType = _sreTypeSystem.GetType(rootInstance.GetType());
}
compiler.IsDesignMode = isDesignMode;
compiler.ParseAndCompile(xaml, uri?.ToString(), null, _sreTypeSystem.CreateTypeBuilder(tb), overrideType);
var created = tb.CreateTypeInfo();
return LoadOrPopulate(created, rootInstance);
}
#endif
static object LoadOrPopulate(Type created, object rootInstance)
{
var isp = Expression.Parameter(typeof(IServiceProvider));
var epar = Expression.Parameter(typeof(object));
var populate = created.GetMethod(AvaloniaXamlIlCompiler.PopulateName);
isp = Expression.Parameter(typeof(IServiceProvider));
var populateCb = Expression.Lambda<Action<IServiceProvider, object>>(
Expression.Call(populate, isp, Expression.Convert(epar, populate.GetParameters()[1].ParameterType)),
isp, epar).Compile();
if (rootInstance == null)
{
var targetType = populate.GetParameters()[1].ParameterType;
var overrideField = targetType.GetField("!XamlIlPopulateOverride",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
if (overrideField != null)
{
overrideField.SetValue(null,
new Action<object>(
target => { populateCb(XamlIlRuntimeHelpers.RootServiceProviderV1, target); }));
try
{
return Activator.CreateInstance(targetType);
}
finally
{
overrideField.SetValue(null, null);
}
}
var createCb = Expression.Lambda<Func<IServiceProvider, object>>(
Expression.Convert(Expression.Call(
created.GetMethod(AvaloniaXamlIlCompiler.BuildName), isp), typeof(object)), isp).Compile();
return createCb(XamlIlRuntimeHelpers.RootServiceProviderV1);
}
else
{
populateCb(XamlIlRuntimeHelpers.RootServiceProviderV1, rootInstance);
return rootInstance;
}
}
public static object Load(Stream stream, Assembly localAssembly, object rootInstance, Uri uri,
bool isDesignMode)
{
string xaml;
using (var sr = new StreamReader(stream))
xaml = sr.ReadToEnd();
#if RUNTIME_XAML_CECIL
return LoadCecil(xaml, localAssembly, rootInstance, uri);
#else
return LoadSre(xaml, localAssembly, rootInstance, uri, isDesignMode);
#endif
}
#if RUNTIME_XAML_CECIL
private static Dictionary<string, (Action<IServiceProvider, object> populate, Func<IServiceProvider, object>
build)>
s_CecilCache =
new Dictionary<string, (Action<IServiceProvider, object> populate, Func<IServiceProvider, object> build)
>();
private static string _cecilEmitDir;
private static CecilTypeSystem _cecilTypeSystem;
private static XamlIlLanguageTypeMappings _cecilMappings;
private static XamlIlXmlnsMappings _cecilXmlns;
private static bool _cecilInitialized;
static void InitializeCecil()
{
if(_cecilInitialized)
return;
var path = Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName;
_cecilEmitDir = Path.Combine(Path.GetDirectoryName(path), "emit");
Directory.CreateDirectory(_cecilEmitDir);
var refs = new[] {path}.Concat(File.ReadAllLines(path + ".refs"));
_cecilTypeSystem = new CecilTypeSystem(refs);
_cecilMappings = AvaloniaXamlIlLanguage.Configure(_cecilTypeSystem);
_cecilXmlns = XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings);
_cecilInitialized = true;
}
private static Dictionary<string, Type> _cecilGeneratedCache = new Dictionary<string, Type>();
static object LoadCecil(string xaml, Assembly localAssembly, object rootInstance, Uri uri)
{
if (uri == null)
throw new InvalidOperationException("Please, go away");
InitializeCecil();
IXamlIlType overrideType = null;
if (rootInstance != null)
{
overrideType = _cecilTypeSystem.GetType(rootInstance.GetType().FullName);
}
var safeUri = uri.ToString()
.Replace(":", "_")
.Replace("/", "_")
.Replace("?", "_")
.Replace("=", "_")
.Replace(".", "_");
if (_cecilGeneratedCache.TryGetValue(safeUri, out var cached))
return LoadOrPopulate(cached, rootInstance);
var asm = _cecilTypeSystem.CreateAndRegisterAssembly(safeUri, new Version(1, 0),
ModuleKind.Dll);
var def = new TypeDefinition("XamlIlLoader", safeUri,
TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
var contextDef = new TypeDefinition("XamlIlLoader", safeUri + "_XamlIlContext",
TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(def);
asm.MainModule.Types.Add(contextDef);
var tb = _cecilTypeSystem.CreateTypeBuilder(def);
var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_cecilTypeSystem,
localAssembly == null ? null : _cecilTypeSystem.FindAssembly(localAssembly.GetName().Name),
_cecilMappings, XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings),
AvaloniaXamlIlLanguage.CustomValueConverter),
_cecilTypeSystem.CreateTypeBuilder(contextDef));
compiler.ParseAndCompile(xaml, uri.ToString(), tb, overrideType);
var asmPath = Path.Combine(_cecilEmitDir, safeUri + ".dll");
using(var f = File.Create(asmPath))
asm.Write(f);
var loaded = Assembly.LoadFile(asmPath)
.GetTypes().First(x => x.Name == safeUri);
_cecilGeneratedCache[safeUri] = loaded;
return LoadOrPopulate(loaded, rootInstance);
}
#endif
}
}

124
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -0,0 +1,124 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlIl;
using XamlIl.Ast;
using XamlIl.Parsers;
using XamlIl.Transform;
using XamlIl.Transform.Transformers;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
class AvaloniaXamlIlCompiler : XamlIlCompiler
{
private readonly XamlIlTransformerConfiguration _configuration;
private readonly IXamlIlType _contextType;
private readonly AvaloniaXamlIlDesignPropertiesTransformer _designTransformer;
private AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration) : base(configuration, true)
{
_configuration = configuration;
void InsertAfter<T>(params IXamlIlAstTransformer[] t)
=> Transformers.InsertRange(Transformers.FindIndex(x => x is T) + 1, t);
void InsertBefore<T>(params IXamlIlAstTransformer[] t)
=> Transformers.InsertRange(Transformers.FindIndex(x => x is T), t);
// Before everything else
Transformers.Insert(0, new XNameTransformer());
Transformers.Insert(1, new IgnoredDirectivesTransformer());
Transformers.Insert(2, _designTransformer = new AvaloniaXamlIlDesignPropertiesTransformer());
Transformers.Insert(3, new AvaloniaBindingExtensionHackTransformer());
// Targeted
InsertBefore<XamlIlPropertyReferenceResolver>(new AvaloniaXamlIlTransformInstanceAttachedProperties());
InsertAfter<XamlIlPropertyReferenceResolver>(new AvaloniaXamlIlAvaloniaPropertyResolver());
InsertBefore<XamlIlContentConvertTransformer>(
new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer(),
new AvaloniaXamlIlTransitionsTypeMetadataTransformer()
);
// After everything else
Transformers.Add(new AddNameScopeRegistration());
Transformers.Add(new AvaloniaXamlIlMetadataRemover());
}
public AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration,
IXamlIlTypeBuilder contextTypeBuilder) : this(configuration)
{
_contextType = CreateContextType(contextTypeBuilder);
}
public AvaloniaXamlIlCompiler(XamlIlTransformerConfiguration configuration,
IXamlIlType contextType) : this(configuration)
{
_contextType = contextType;
}
public const string PopulateName = "__AvaloniaXamlIlPopulate";
public const string BuildName = "__AvaloniaXamlIlBuild";
public bool IsDesignMode
{
get => _designTransformer.IsDesignMode;
set => _designTransformer.IsDesignMode = value;
}
public void ParseAndCompile(string xaml, string baseUri, IFileSource fileSource, IXamlIlTypeBuilder tb, IXamlIlType overrideRootType)
{
var parsed = XDocumentXamlIlParser.Parse(xaml, new Dictionary<string, string>
{
{XamlNamespaces.Blend2008, XamlNamespaces.Blend2008}
});
var rootObject = (XamlIlAstObjectNode)parsed.Root;
var classDirective = rootObject.Children
.OfType<XamlIlAstXmlDirective>().FirstOrDefault(x =>
x.Namespace == XamlNamespaces.Xaml2006
&& x.Name == "Class");
var rootType =
classDirective != null ?
new XamlIlAstClrTypeReference(classDirective,
_configuration.TypeSystem.GetType(((XamlIlAstTextNode)classDirective.Values[0]).Text),
false) :
XamlIlTypeReferenceResolver.ResolveType(CreateTransformationContext(parsed, true),
(XamlIlAstXmlTypeReference)rootObject.Type, true);
if (overrideRootType != null)
{
if (!rootType.Type.IsAssignableFrom(overrideRootType))
throw new XamlIlLoadException(
$"Unable to substitute {rootType.Type.GetFqn()} with {overrideRootType.GetFqn()}", rootObject);
rootType = new XamlIlAstClrTypeReference(rootObject, overrideRootType, false);
}
rootObject.Type = rootType;
Transform(parsed);
Compile(parsed, tb, _contextType, PopulateName, BuildName, "__AvaloniaXamlIlNsInfo", baseUri, fileSource);
}
}
}

176
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@ -0,0 +1,176 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlIl;
using XamlIl.Ast;
using XamlIl.Transform;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
/*
This file is used in the build task.
ONLY use types from netstandard and XamlIl. NO dependencies on Avalonia are allowed. Only strings.
No, nameof isn't welcome here either
*/
class AvaloniaXamlIlLanguage
{
public static XamlIlLanguageTypeMappings Configure(IXamlIlTypeSystem typeSystem)
{
var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers");
var assignBindingAttribute = typeSystem.GetType("Avalonia.Data.AssignBindingAttribute");
var bindingType = typeSystem.GetType("Avalonia.Data.IBinding");
var rv = new XamlIlLanguageTypeMappings(typeSystem)
{
SupportInitialize = typeSystem.GetType("System.ComponentModel.ISupportInitialize"),
XmlnsAttributes =
{
typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"),
typeSystem.FindType("Portable.Xaml.Markup.XmlnsDefinitionAttribute")
},
ContentAttributes =
{
typeSystem.GetType("Avalonia.Metadata.ContentAttribute")
},
ProvideValueTarget = typeSystem.GetType("Portable.Xaml.Markup.IProvideValueTarget"),
RootObjectProvider = typeSystem.GetType("Portable.Xaml.IRootObjectProvider"),
UriContextProvider = typeSystem.GetType("Portable.Xaml.Markup.IUriContext"),
ParentStackProvider =
typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.IAvaloniaXamlIlParentStackProvider"),
XmlNamespaceInfoProvider =
typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.IAvaloniaXamlIlXmlNamespaceInfoProvider"),
DeferredContentPropertyAttributes = {typeSystem.GetType("Avalonia.Metadata.TemplateContentAttribute")},
DeferredContentExecutorCustomization =
runtimeHelpers.FindMethod(m => m.Name == "DeferredTransformationFactoryV1"),
UsableDuringInitializationAttributes =
{
typeSystem.GetType("Portable.Xaml.Markup.UsableDuringInitializationAttribute"),
typeSystem.GetType("Avalonia.Metadata.UsableDuringInitializationAttribute"),
},
InnerServiceProviderFactoryMethod =
runtimeHelpers.FindMethod(m => m.Name == "CreateInnerServiceProviderV1"),
ProvideValueTargetPropertyEmitter = XamlIlAvaloniaPropertyHelper.Emit,
};
rv.CustomAttributeResolver = new AttributeResolver(typeSystem, rv);
return rv;
}
class AttributeResolver : IXamlIlCustomAttributeResolver
{
private readonly IXamlIlType _typeConverterAttribute;
private readonly List<KeyValuePair<IXamlIlType, IXamlIlType>> _converters =
new List<KeyValuePair<IXamlIlType, IXamlIlType>>();
private readonly IXamlIlType _avaloniaList;
private readonly IXamlIlType _avaloniaListConverter;
public AttributeResolver(IXamlIlTypeSystem typeSystem, XamlIlLanguageTypeMappings mappings)
{
_typeConverterAttribute = mappings.TypeConverterAttributes.First();
void AddType(IXamlIlType type, IXamlIlType conv)
=> _converters.Add(new KeyValuePair<IXamlIlType, IXamlIlType>(type, conv));
void Add(string type, string conv)
=> AddType(typeSystem.GetType(type), typeSystem.GetType(conv));
//Add("Avalonia.AvaloniaProperty","Avalonia.Markup.Xaml.Converters.AvaloniaPropertyTypeConverter");
Add("Avalonia.Media.Imaging.IBitmap","Avalonia.Markup.Xaml.Converters.BitmapTypeConverter");
var ilist = typeSystem.GetType("System.Collections.Generic.IList`1");
AddType(ilist.MakeGenericType(typeSystem.GetType("Avalonia.Point")),
typeSystem.GetType("Avalonia.Markup.Xaml.Converters.PointsListTypeConverter"));
Add("Avalonia.Controls.Templates.IMemberSelector",
"Avalonia.Markup.Xaml.Converters.MemberSelectorTypeConverter");
Add("Avalonia.Styling.Selector","Avalonia.Markup.Xaml.Converters.SelectorTypeConverter");
Add("Avalonia.Controls.WindowIcon","Avalonia.Markup.Xaml.Converters.IconTypeConverter");
Add("System.Globalization.CultureInfo", "System.ComponentModel.CultureInfoConverter");
Add("System.Uri", "Avalonia.Markup.Xaml.Converters.AvaloniaUriTypeConverter");
Add("System.TimeSpan", "Avalonia.Markup.Xaml.Converters.TimeSpanTypeConverter");
Add("Avalonia.Media.FontFamily","Avalonia.Markup.Xaml.Converters.FontFamilyTypeConverter");
_avaloniaList = typeSystem.GetType("Avalonia.Collections.AvaloniaList`1");
_avaloniaListConverter = typeSystem.GetType("Avalonia.Collections.AvaloniaListConverter`1");
}
IXamlIlType LookupConverter(IXamlIlType type)
{
foreach(var p in _converters)
if (p.Key.Equals(type))
return p.Value;
if (type.GenericTypeDefinition?.Equals(_avaloniaList) == true)
return _avaloniaListConverter.MakeGenericType(type.GenericArguments[0]);
return null;
}
class ConstructedAttribute : IXamlIlCustomAttribute
{
public bool Equals(IXamlIlCustomAttribute other) => false;
public IXamlIlType Type { get; }
public List<object> Parameters { get; }
public Dictionary<string, object> Properties { get; }
public ConstructedAttribute(IXamlIlType type, List<object> parameters, Dictionary<string, object> properties)
{
Type = type;
Parameters = parameters ?? new List<object>();
Properties = properties ?? new Dictionary<string, object>();
}
}
public IXamlIlCustomAttribute GetCustomAttribute(IXamlIlType type, IXamlIlType attributeType)
{
if (attributeType.Equals(_typeConverterAttribute))
{
var conv = LookupConverter(type);
if (conv != null)
return new ConstructedAttribute(_typeConverterAttribute, new List<object>() {conv}, null);
}
return null;
}
public IXamlIlCustomAttribute GetCustomAttribute(IXamlIlProperty property, IXamlIlType attributeType)
{
return null;
}
}
public static bool CustomValueConverter(XamlIlAstTransformationContext context,
IXamlIlAstValueNode node, IXamlIlType type, out IXamlIlAstValueNode result)
{
if (type.FullName == "System.TimeSpan"
&& node is XamlIlAstTextNode tn
&& !tn.Text.Contains(":"))
{
var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture);
result = new XamlIlStaticOrTargetedReturnMethodCallNode(tn,
type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double),
new[]
{
new XamlIlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds)
});
return true;
}
if (type.FullName == "Avalonia.AvaloniaProperty")
{
var scope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
if (scope == null)
throw new XamlIlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
if (!(node is XamlIlAstTextNode text))
throw new XamlIlLoadException("Property should be a text node", node);
result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text);
return true;
}
result = null;
return false;
}
}
}

94
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AddNameScopeRegistration.cs

@ -0,0 +1,94 @@
using System;
using System.Linq;
using XamlIl.Ast;
using XamlIl.Transform;
using XamlIl.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class AddNameScopeRegistration : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (node is XamlIlPropertyAssignmentNode pa
&& pa.Property.Name == "Name"
&& pa.Property.DeclaringType.FullName == "Avalonia.StyledElement")
{
if (context.ParentNodes().FirstOrDefault() is XamlIlManipulationGroupNode mg
&& mg.Children.OfType<ScopeRegistrationNode>().Any())
return node;
IXamlIlAstValueNode value = null;
for (var c = 0; c < pa.Values.Count; c++)
if (pa.Values[c].Type.GetClrType().Equals(context.Configuration.WellKnownTypes.String))
{
value = pa.Values[c];
if (!(value is XamlIlAstTextNode))
{
var local = new XamlIlAstCompilerLocalNode(value);
// Wrap original in local initialization
pa.Values[c] = new XamlIlAstLocalInitializationNodeEmitter(value, value, local);
// Use local
value = local;
}
break;
}
if (value != null)
return new XamlIlManipulationGroupNode(pa)
{
Children =
{
pa,
new ScopeRegistrationNode(value)
}
};
}
return node;
}
class ScopeRegistrationNode : XamlIlAstNode, IXamlIlAstManipulationNode, IXamlIlAstEmitableNode
{
public IXamlIlAstValueNode Value { get; set; }
public ScopeRegistrationNode(IXamlIlAstValueNode value) : base(value)
{
Value = value;
}
public override void VisitChildren(IXamlIlAstVisitor visitor)
=> Value = (IXamlIlAstValueNode)Value.Visit(visitor);
public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
{
var exts = context.Configuration.TypeSystem.GetType("Avalonia.Controls.NameScopeExtensions");
var findNameScope = exts.FindMethod(m => m.Name == "FindNameScope");
var registerMethod = findNameScope.ReturnType.FindMethod(m => m.Name == "Register");
using (var targetLoc = context.GetLocal(context.Configuration.WellKnownTypes.Object))
using (var nameScopeLoc = context.GetLocal(findNameScope.ReturnType))
{
var exit = codeGen.DefineLabel();
codeGen
// var target = {pop}
.Stloc(targetLoc.Local)
// var scope = target.FindNameScope()
.Ldloc(targetLoc.Local)
.Castclass(findNameScope.Parameters[0])
.EmitCall(findNameScope)
.Stloc(nameScopeLoc.Local)
// if({scope} != null) goto call;
.Ldloc(nameScopeLoc.Local)
.Brfalse(exit)
.Ldloc(nameScopeLoc.Local);
context.Emit(Value, codeGen, Value.Type.GetClrType());
codeGen
.Ldloc(targetLoc.Local)
.EmitCall(registerMethod)
.MarkLabel(exit);
}
return XamlIlNodeEmitResult.Void(1);
}
}
}
}

20
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaBindingExtensionHackTransformer.cs

@ -0,0 +1,20 @@
using XamlIl.Ast;
using XamlIl.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class AvaloniaBindingExtensionHackTransformer : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
// Our code base expects XAML parser to prefer `FooExtension` to `Foo` even with `<Foo>` syntax
// This is the legacy of Portable.Xaml, so we emulate that behavior here
if (node is XamlIlAstXmlTypeReference tref
&& tref.Name == "Binding"
&& tref.XmlNamespace == "https://github.com/avaloniaui")
tref.IsMarkupExtension = true;
return node;
}
}
}

24
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/Transformers/AvaloniaXamlIlAvaloniaPropertyResolver.cs

@ -0,0 +1,24 @@
using System.Linq;
using XamlIl.Ast;
using XamlIl.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
class AvaloniaXamlIlAvaloniaPropertyResolver : IXamlIlAstTransformer
{
public IXamlIlAstNode Transform(XamlIlAstTransformationContext context, IXamlIlAstNode node)
{
if (node is XamlIlAstClrProperty prop)
{
var n = prop.Name + "Property";
var field =
prop.DeclaringType.Fields
.FirstOrDefault(f => f.Name == n);
if (field != null)
return new XamlIlAvaloniaProperty(prop, field, context.GetAvaloniaTypes());
}
return node;
}
}
}

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

Loading…
Cancel
Save