Browse Source

Generate asset resource info with paths

pull/2104/head
Nikita Tsukanov 7 years ago
parent
commit
343905ebb3
  1. 27
      Avalonia.sln
  2. 3
      Avalonia.sln.DotSettings
  3. 8
      build/BuildTargets.targets
  4. 22
      packages/Avalonia/Avalonia.csproj
  5. 3
      packages/Avalonia/Avalonia.props
  6. 3
      packages/Avalonia/Avalonia.targets
  7. 3
      packages/Avalonia/AvaloniaBuildTasks.props
  8. 39
      packages/Avalonia/AvaloniaBuildTasks.targets
  9. 10
      samples/ControlCatalog/App.xaml
  10. 8
      samples/ControlCatalog/ControlCatalog.csproj
  11. 6
      samples/ControlCatalog/DecoratedWindow.xaml
  12. 3
      samples/ControlCatalog/MainView.xaml
  13. 8
      samples/ControlCatalog/MainWindow.xaml
  14. 4
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  15. 4
      samples/ControlCatalog/Pages/BorderPage.xaml
  16. 3
      samples/ControlCatalog/Pages/ButtonPage.xaml
  17. 3
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml
  18. 3
      samples/ControlCatalog/Pages/CalendarPage.xaml
  19. 4
      samples/ControlCatalog/Pages/CanvasPage.xaml
  20. 10
      samples/ControlCatalog/Pages/CarouselPage.xaml
  21. 3
      samples/ControlCatalog/Pages/CheckBoxPage.xaml
  22. 6
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  23. 3
      samples/ControlCatalog/Pages/DatePickerPage.xaml
  24. 4
      samples/ControlCatalog/Pages/DialogsPage.xaml
  25. 4
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  26. 4
      samples/ControlCatalog/Pages/DropDownPage.xaml
  27. 4
      samples/ControlCatalog/Pages/ExpanderPage.xaml
  28. 14
      samples/ControlCatalog/Pages/ImagePage.xaml
  29. 5
      samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml
  30. 4
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  31. 6
      samples/ControlCatalog/Pages/MenuPage.xaml
  32. 3
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  33. 6
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  34. 5
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  35. 6
      samples/ControlCatalog/Pages/SliderPage.xaml
  36. 15
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  37. 6
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  38. 6
      samples/ControlCatalog/Pages/TreeViewPage.xaml
  39. 3
      samples/ControlCatalog/SideBar.xaml
  40. 3
      src/Avalonia.Base/Avalonia.Base.csproj
  41. 2
      src/Avalonia.Base/Platform/IAssetLoader.cs
  42. 58
      src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs
  43. 20
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  44. 19
      src/Avalonia.Build.Tasks/Extensions.cs
  45. 158
      src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
  46. 20
      src/Avalonia.Build.Tasks/XamlFileInfo.cs
  47. 3
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  48. 22
      src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs
  49. 2
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  50. 3
      src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
  51. 38
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  52. 25
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs
  53. 18
      src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs
  54. 22
      src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs
  55. 12
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs
  56. 172
      src/Shared/PlatformSupport/AssetLoader.cs
  57. 5
      tests/Avalonia.UnitTests/MockAssetLoader.cs

27
Avalonia.sln

@ -147,6 +147,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Splat.props = build\Splat.props
build\System.Memory.props = build\System.Memory.props
build\XUnit.props = build\XUnit.props
build\BuildTargets.targets = build\BuildTargets.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
@ -188,6 +189,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia", "packages\Avalon
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Desktop", "src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{3C471044-3640-45E3-B1B2-16D2FF8399EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks", "src\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj", "{BF28998D-072C-439A-AFBB-2FE5021241E0}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -1687,6 +1690,30 @@ Global
{3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhone.Build.0 = Release|Any CPU
{3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhone.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhone.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.Build.0 = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.ActiveCfg = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

3
Avalonia.sln.DotSettings

@ -35,4 +35,5 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="s_" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Avalonia/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

8
build/BuildTargets.targets

@ -0,0 +1,8 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\src\Avalonia.Build.Tasks\bin\$(Configuration)\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>true</AvaloniaUseExternalMSBuild>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\..\packages\Avalonia\AvaloniaBuildTasks.props"/>
<Import Project="$(MSBuildThisFileDirectory)\..\packages\Avalonia\AvaloniaBuildTasks.targets"/>
</Project>

22
packages/Avalonia/Avalonia.csproj

@ -5,6 +5,8 @@
<ItemGroup>
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" EmbedReference="false" />
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" />
</ItemGroup>
<PropertyGroup>
@ -27,14 +29,22 @@
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>
<_PackageFiles Include="Avalonia.props">
<PackagePath>build/Avalonia.props</PackagePath>
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>
</ItemGroup>
</Target>
<ItemGroup>
<Content Include="*.props">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
<Content Include="*.targets">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
<Content Include="../../src/Avalonia.Build.Tasks/bin/$(Configuration)/netstandard2.0/Avalonia.Build.Tasks.dll">
<Pack>true</Pack>
<PackagePath>tools\</PackagePath>
</Content>
</ItemGroup>
<Import Project="..\..\build\SharedVersion.props" />
<Import Project="..\..\build\NetFX.props" />
<Import Project="..\..\build\CoreLibraries.props" />

3
packages/Avalonia/Avalonia.props

@ -2,5 +2,8 @@
<PropertyGroup>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>
</Project>

3
packages/Avalonia/Avalonia.targets

@ -0,0 +1,3 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.targets"/>
</Project>

3
packages/Avalonia/AvaloniaBuildTasks.props

@ -0,0 +1,3 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
</Project>

39
packages/Avalonia/AvaloniaBuildTasks.targets

@ -0,0 +1,39 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild)</_AvaloniaUseExternalMSBuild>
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false</_AvaloniaUseExternalMSBuild>
</PropertyGroup>
<UsingTask TaskName="GenerateAvaloniaResourcesTask"
AssemblyFile="$(AvaloniaBuildTasksLocation)"
/>
<Target Name="AddAvaloniaResources" BeforeTargets="ResolveReferences">
<PropertyGroup>
<AvaloniaResourcesTemporaryFilePath Condition="'$(AvaloniaResourcesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/resources</AvaloniaResourcesTemporaryFilePath>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="$(AvaloniaResourcesTemporaryFilePath)">
<LogicalName>!AvaloniaResources</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
<Target Name="GenerateAvaloniaResources"
BeforeTargets="CoreCompile"
Inputs="@(AvaloniaResource);$(MSBuildProjectFile)"
Outputs="$(AvaloniaResourcesTemporaryFilePath)"
DependsOnTargets="$(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources">
<GenerateAvaloniaResourcesTask
Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'"
Output="$(AvaloniaResourcesTemporaryFilePath)"
Root="$(MSBuildProjectDirectory)"
Resources="@(AvaloniaResource)"
EmbeddedResources="@(EmbeddedResources)"/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration)"/>
</Target>
</Project>

10
samples/ControlCatalog/App.xaml

@ -1,7 +1,9 @@
<Application xmlns="https://github.com/avaloniaui">
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.App">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="res:asm:Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="res:asm:Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="FontWeight" Value="Medium"/>
@ -12,6 +14,6 @@
<Style Selector="TextBlock.h3">
<Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}"/>
</Style>
<StyleInclude Source="resm:ControlCatalog.SideBar.xaml"/>
<StyleInclude Source="/SideBar.xaml"/>
</Application.Styles>
</Application>

8
samples/ControlCatalog/ControlCatalog.csproj

@ -6,10 +6,11 @@
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<EmbeddedResource Include="**\*.xaml">
<AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Assets\*" />
</AvaloniaResource>
<AvaloniaResource Include="Assets\*" />
<AvaloniaResource Include="Assets\Fonts\*" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\Fonts\SourceSansPro-Bold.ttf" />
@ -24,4 +25,5 @@
</ItemGroup>
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\BuildTargets.targets" />
</Project>

6
samples/ControlCatalog/DecoratedWindow.xaml

@ -1,6 +1,8 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.DecoratedWindow"
Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico"
Icon="/Assets/test_icon.ico"
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False">
<Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5">
<DockPanel Grid.Column="1" Grid.Row="1" >
@ -30,4 +32,4 @@
<Border Name="Bottom" Background="Blue" Grid.Row="2" Grid.Column="1" />
<Border Name="Left" Background="Blue" Grid.Row="1" />
</Grid>
</Window>
</Window>

3
samples/ControlCatalog/MainView.xaml

@ -1,6 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainView">
<TabControl Classes="sidebar" Name="Sidebar">
<TabControl.PageTransition>
<CrossFade Duration="0.25"/>

8
samples/ControlCatalog/MainWindow.xaml

@ -1,6 +1,8 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog"
xmlns:local="clr-namespace:ControlCatalog">
Icon="/Assets/test_icon.ico?assembly=ControlCatalog"
xmlns:local="clr-namespace:ControlCatalog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainWindow">
<local:MainView/>
</Window>
</Window>

4
samples/ControlCatalog/Pages/AutoCompleteBoxPage.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="ControlCatalog.Pages.AutoCompleteBoxPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">AutoCompleteBox</TextBlock>
<TextBlock Classes="h2">A control into which the user can input text</TextBlock>

4
samples/ControlCatalog/Pages/BorderPage.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="ControlCatalog.Pages.BorderPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Border</TextBlock>
<TextBlock Classes="h2">A control which decorates a child with a border and background</TextBlock>

3
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ButtonPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Button</TextBlock>
<TextBlock Classes="h2">A button control</TextBlock>

3
samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ButtonSpinnerPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ButtonSpinner</TextBlock>

3
samples/ControlCatalog/Pages/CalendarPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CalendarPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Calendar</TextBlock>
<TextBlock Classes="h2">A calendar control for selecting dates</TextBlock>

4
samples/ControlCatalog/Pages/CanvasPage.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="ControlCatalog.Pages.CanvasPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Canvas</TextBlock>
<TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</TextBlock>

10
samples/ControlCatalog/Pages/CarouselPage.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="ControlCatalog.Pages.CarouselPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Carousel</TextBlock>
<TextBlock Classes="h2">An items control that displays its items as pages that fill the control.</TextBlock>
@ -11,9 +13,9 @@
<Carousel.PageTransition>
<PageSlide Duration="0.25" Orientation="Vertical" />
</Carousel.PageTransition>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"/>
<Image Source="resm:ControlCatalog.Assets.hirsch-899118_640.jpg"/>
<Image Source="resm:ControlCatalog.Assets.maple-leaf-888807_640.jpg"/>
<Image Source="/Assets/delicate-arch-896885_640.jpg"/>
<Image Source="/Assets/hirsch-899118_640.jpg"/>
<Image Source="/Assets/maple-leaf-888807_640.jpg"/>
</Carousel>
<Button Name="right" VerticalAlignment="Center" Padding="20">
<Path Data="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" Fill="Black"/>

3
samples/ControlCatalog/Pages/CheckBoxPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CheckBoxPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">CheckBox</TextBlock>
<TextBlock Classes="h2">A check box control</TextBlock>

6
samples/ControlCatalog/Pages/ContextMenuPage.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="ControlCatalog.Pages.ContextMenuPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Context Menu</TextBlock>
<TextBlock Classes="h2">A right click menu that can be applied to any control.</TextBlock>
@ -19,7 +21,7 @@
</MenuItem>
<MenuItem Header="Menu Item with _Icon">
<MenuItem.Icon>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/>
<Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item with _Checkbox">

3
samples/ControlCatalog/Pages/DatePickerPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DatePickerPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DatePicker</TextBlock>
<TextBlock Classes="h2">A control for selecting dates with a calendar drop-down</TextBlock>

4
samples/ControlCatalog/Pages/DialogsPage.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="ControlCatalog.Pages.DialogsPage">
<StackPanel Orientation="Vertical" Spacing="4" Margin="4">
<Button Name="OpenFile">Open File</Button>
<Button Name="SaveFile">Save File</Button>

4
samples/ControlCatalog/Pages/DragAndDropPage.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="ControlCatalog.Pages.DragAndDropPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Drag+Drop</TextBlock>
<TextBlock Classes="h2">Example of Drag+Drop capabilities</TextBlock>

4
samples/ControlCatalog/Pages/DropDownPage.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="ControlCatalog.Pages.DropDownPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DropDown</TextBlock>
<TextBlock Classes="h2">A drop-down list.</TextBlock>

4
samples/ControlCatalog/Pages/ExpanderPage.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="ControlCatalog.Pages.ExpanderPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Expander</TextBlock>
<TextBlock Classes="h2">Expands to show nested content</TextBlock>

14
samples/ControlCatalog/Pages/ImagePage.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="ControlCatalog.Pages.ImagePage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Image</TextBlock>
<TextBlock Classes="h2">Displays an image</TextBlock>
@ -9,28 +11,28 @@
Spacing="16">
<StackPanel Orientation="Vertical">
<TextBlock>No Stretch</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"
<Image Source="/Assets/delicate-arch-896885_640.jpg"
Width="100" Height="200"
Stretch="None"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Fill</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"
<Image Source="/Assets/delicate-arch-896885_640.jpg"
Width="100" Height="200"
Stretch="Fill"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Uniform</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"
<Image Source="/Assets/delicate-arch-896885_640.jpg"
Width="100" Height="200"
Stretch="Uniform"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>UniformToFill</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"
<Image Source="/Assets/delicate-arch-896885_640.jpg"
Width="100" Height="200"
Stretch="UniformToFill"/>
</StackPanel>
@ -40,4 +42,4 @@
<Image Name="Icon" Width="100" Height="200" Stretch="None" />
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

5
samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.LayoutTransformControlPage">
<DockPanel>
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto" Margin="16" DockPanel.Dock="Top">
<TextBlock Grid.Column="0" Grid.Row="0">Rotation</TextBlock>
@ -23,4 +24,4 @@
</LayoutTransformControl>
</Grid>
</DockPanel>
</UserControl>
</UserControl>

4
samples/ControlCatalog/Pages/ListBoxPage.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="ControlCatalog.Pages.ListBoxPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ListBox</TextBlock>
<TextBlock Classes="h2">Hosts a collection of ListBoxItem.</TextBlock>

6
samples/ControlCatalog/Pages/MenuPage.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="ControlCatalog.Pages.MenuPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Menu</TextBlock>
<TextBlock Classes="h2">A window menu</TextBlock>
@ -19,7 +21,7 @@
</MenuItem>
<MenuItem Header="Menu Item with _Icon">
<MenuItem.Icon>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/>
<Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item with _Checkbox">

3
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.NumericUpDownPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Margin="2" Classes="h1">Numeric up-down control</TextBlock>
<TextBlock Margin="2" Classes="h2" TextWrapping="Wrap">Numeric up-down control provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel.</TextBlock>

6
samples/ControlCatalog/Pages/ProgressBarPage.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="ControlCatalog.Pages.ProgressBarPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ProgressBar</TextBlock>
<TextBlock Classes="h2">A progress bar control</TextBlock>
@ -21,4 +23,4 @@
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

5
samples/ControlCatalog/Pages/RadioButtonPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.RadioButtonPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">RadioButton</TextBlock>
<TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock>
@ -37,4 +38,4 @@
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

6
samples/ControlCatalog/Pages/SliderPage.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="ControlCatalog.Pages.SliderPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Slider</TextBlock>
<TextBlock Classes="h2">A control that lets the user select from a range of values by moving a Thumb control along a Track.</TextBlock>
@ -18,4 +20,4 @@
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

15
samples/ControlCatalog/Pages/TextBoxPage.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="ControlCatalog.Pages.TextBoxPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TextBox</TextBlock>
<TextBlock Classes="h2">A control into which the user can input text</TextBlock>
@ -33,11 +35,20 @@
Text="Multiline TextBox with no TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8">
<TextBlock Classes="h2">resm fonts</TextBlock>
<TextBox Width="200" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-Italic.ttf?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-*.ttf?assembly=ControlCatalog#Source Sans Pro"/>
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8">
<TextBlock Classes="h2">res fonts</TextBlock>
<TextBox Width="200" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="res:asm:ControlCatalog/Assets/Fonts#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="res:asm:ControlCatalog/Assets/Fonts#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="res:asm:ControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="res:asm:ControlCatalog/Assets/Fonts/SourceSansPro-*.ttf#Source Sans Pro"/>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

6
samples/ControlCatalog/Pages/ToolTipPage.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="ControlCatalog.Pages.ToolTipPage">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Classes="h1">ToolTip</TextBlock>
@ -38,4 +40,4 @@
</Border>
</Grid>
</StackPanel>
</UserControl>
</UserControl>

6
samples/ControlCatalog/Pages/TreeViewPage.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="ControlCatalog.Pages.TreeViewPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TreeView</TextBlock>
<TextBlock Classes="h2">Displays a hierachical tree of data.</TextBlock>
@ -16,4 +18,4 @@
</TreeView>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

3
samples/ControlCatalog/SideBar.xaml

@ -1,5 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.SideBar">
<Style Selector="TabControl.sidebar">
<Setter Property="Template">
<ControlTemplate>

3
src/Avalonia.Base/Avalonia.Base.csproj

@ -6,6 +6,9 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj"/>
</ItemGroup>
<Import Project="..\..\build\Base.props" />
<Import Project="..\..\build\Binding.props" />
<Import Project="..\..\build\Rx.props" />

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

@ -66,6 +66,6 @@ namespace Avalonia.Platform
/// </summary>
/// <param name="uri">The URI.</param>
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
IEnumerable<(string absolutePath, Assembly assembly)> GetAssets(Uri uri);
IEnumerable<Uri> GetAssets(Uri uri, Uri baseUri);
}
}

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

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
// ReSharper disable AssignNullToNotNullAttribute
namespace Avalonia.Utilities
{
#if !BUILDTASK
public
#endif
static class AvaloniaResourcesIndexReaderWriter
{
private const int LastKnownVersion = 1;
public static List<AvaloniaResourcesIndexEntry> Read(Stream stream)
{
var ver = new BinaryReader(stream).ReadInt32();
if (ver > LastKnownVersion)
throw new Exception("Resources index format version is not known");
var index = (AvaloniaResourcesIndex)
new DataContractSerializer(typeof(AvaloniaResourcesIndex)).ReadObject(stream);
return index.Entries;
}
public static void Write(Stream stream, List<AvaloniaResourcesIndexEntry> entries)
{
new BinaryWriter(stream).Write(LastKnownVersion);
new DataContractSerializer(typeof(AvaloniaResourcesIndex)).WriteObject(stream,
new AvaloniaResourcesIndex()
{
Entries = entries
});
}
}
[DataContract]
public class AvaloniaResourcesIndex
{
[DataMember]
public List<AvaloniaResourcesIndexEntry> Entries { get; set; } = new List<AvaloniaResourcesIndexEntry>();
}
[DataContract]
public class AvaloniaResourcesIndexEntry
{
[DataMember]
public string Path { get; set; }
[DataMember]
public int Offset { get; set; }
[DataMember]
public int Size { get; set; }
}
}

20
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="../Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs">
<Link>Shared/AvaloniaResourcesIndex.cs</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs">
<Link>Shared/AvaloniaResourceXamlInfo.cs</Link>
</Compile>
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" />
</ItemGroup>
<!-- Disable built-in Pack target -->
<Target Name="Pack"/>
</Project>

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

@ -0,0 +1,19 @@
using Microsoft.Build.Framework;
namespace Avalonia.Build.Tasks
{
public static class Extensions
{
public static void LogError(this IBuildEngine engine, string file, string message)
{
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "0000", file ?? "", 0, 0, 0, 0, message, "",
"Avalonia"));
}
public static void LogWarning(this IBuildEngine engine, string file, string message)
{
engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", "0000", file ?? "", 0, 0, 0, 0, message, "",
"Avalonia"));
}
}
}

158
src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Utilities;
using Microsoft.Build.Framework;
using SPath=System.IO.Path;
namespace Avalonia.Build.Tasks
{
public class GenerateAvaloniaResourcesTask : ITask
{
[Required]
public ITaskItem[] Resources { get; set; }
[Required]
public string Root { get; set; }
[Required]
public string Output { get; set; }
[Required]
public ITaskItem[] EmbeddedResources { get; set; }
class Source
{
public string Path { get; set; }
public int Size { get; set; }
private byte[] _data;
private string _sourcePath;
public Source(string file, string root)
{
file = SPath.GetFullPath(file);
root = SPath.GetFullPath(root);
var fileUri = new Uri(file, UriKind.Absolute);
var rootUri = new Uri(root, UriKind.Absolute);
rootUri = new Uri(rootUri.ToString().TrimEnd('/') + '/');
Path = '/' + rootUri.MakeRelativeUri(fileUri).ToString().TrimStart('/');
_sourcePath = file;
Size = (int)new FileInfo(_sourcePath).Length;
}
public string SystemPath => _sourcePath ?? Path;
public Source(string path, byte[] data)
{
Path = path;
_data = data;
Size = data.Length;
}
public Stream Open()
{
if (_data != null)
return new MemoryStream(_data, false);
return File.OpenRead(_sourcePath);
}
public string ReadAsString()
{
if (_data != null)
return Encoding.UTF8.GetString(_data);
return File.ReadAllText(_sourcePath);
}
}
List<Source> BuildResourceSources() => Resources.Select(r => new Source(r.ItemSpec, Root)).ToList();
void Pack(Stream output, List<Source> sources)
{
var offsets = new Dictionary<Source, int>();
var coffset = 0;
foreach (var s in sources)
{
offsets[s] = coffset;
coffset += s.Size;
}
var index = sources.Select(s => new AvaloniaResourcesIndexEntry
{
Path = s.Path,
Size = s.Size,
Offset = offsets[s]
}).ToList();
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)
{
using(var input = s.Open())
input.CopyTo(output);
}
}
bool PreProcessXamlFiles(List<Source> sources)
{
var typeToXamlIndex = new Dictionary<string, string>();
foreach (var s in sources.ToList())
{
if (s.Path.ToLowerInvariant().EndsWith(".xaml") || s.Path.ToLowerInvariant().EndsWith(".paml"))
{
XamlFileInfo info;
try
{
info = XamlFileInfo.Parse(s.ReadAsString());
}
catch(Exception e)
{
BuildEngine.LogError(s.SystemPath, "File doesn't contain valid XAML: " + e);
return false;
}
if (info.XClass != null)
{
if (typeToXamlIndex.ContainsKey(info.XClass))
{
BuildEngine.LogError(s.SystemPath,
$"Duplicate x:Class directive, {info.XClass} is already used in {typeToXamlIndex[info.XClass]}");
return false;
}
typeToXamlIndex[info.XClass] = s.Path;
}
}
}
var xamlInfo = new AvaloniaResourceXamlInfo
{
ClassToResourcePathIndex = typeToXamlIndex
};
var ms = new MemoryStream();
new DataContractSerializer(typeof(AvaloniaResourceXamlInfo)).WriteObject(ms, xamlInfo);
sources.Add(new Source("/!AvaloniaResourceXamlInfo", ms.ToArray()));
return true;
}
public bool Execute()
{
foreach(var r in EmbeddedResources.Where(r=>r.ItemSpec.EndsWith(".xaml")||r.ItemSpec.EndsWith(".paml")))
BuildEngine.LogWarning(r.ItemSpec, "XAML file is packed using legacy EmbeddedResource/resm scheme, relative URIs won't work");
var resources = BuildResourceSources();
if (!PreProcessXamlFiles(resources))
return false;
var dir = Path.GetDirectoryName(Output);
Directory.CreateDirectory(dir);
using (var file = File.Create(Output))
Pack(file, resources);
return true;
}
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
}
}

20
src/Avalonia.Build.Tasks/XamlFileInfo.cs

@ -0,0 +1,20 @@
using System.Xml.Linq;
namespace Avalonia.Build.Tasks
{
public class XamlFileInfo
{
public string XClass { get; set; }
public static XamlFileInfo Parse(string data)
{
var xdoc = XDocument.Parse(data);
var xclass = xdoc.Root.Attribute(XName.Get("Class", "http://schemas.microsoft.com/winfx/2006/xaml"));
return new XamlFileInfo
{
XClass = xclass?.Value
};
}
}
}

3
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -13,7 +13,10 @@
<ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<AvaloniaResource Include="DefaultTheme.xaml"/>
<AvaloniaResource Include="Accents/*.xaml"/>
</ItemGroup>
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\BuildTargets.targets"/>
<Import Project="..\..\build\Rx.props" />
</Project>

22
src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs

@ -32,11 +32,11 @@ namespace Avalonia.Media.Fonts
/// <returns></returns>
private static IEnumerable<Uri> GetFontAssetsByLocation(Uri location)
{
var availableAssets = s_assetLoader.GetAssets(location);
var availableAssets = s_assetLoader.GetAssets(location, null);
var matchingAssets = availableAssets.Where(x => x.absolutePath.EndsWith(".ttf") || x.absolutePath.EndsWith(".otf"));
var matchingAssets = availableAssets.Where(x => x.AbsolutePath.EndsWith(".ttf") || x.AbsolutePath.EndsWith(".otf"));
return matchingAssets.Select(x => GetAssetUri(x.absolutePath, x.assembly));
return matchingAssets;
}
/// <summary>
@ -48,25 +48,15 @@ namespace Avalonia.Media.Fonts
/// <returns></returns>
private static IEnumerable<Uri> GetFontAssetsByFileName(Uri location, string fileName)
{
var availableResources = s_assetLoader.GetAssets(location);
var availableResources = s_assetLoader.GetAssets(location, null);
var compareTo = location.AbsolutePath + "." + fileName.Split('*').First();
var matchingResources =
availableResources.Where(x => x.absolutePath.Contains(compareTo) && (x.absolutePath.EndsWith(".ttf") || x.absolutePath.EndsWith(".otf")));
availableResources.Where(x => x.AbsolutePath.Contains(compareTo) && (x.AbsolutePath.EndsWith(".ttf") || x.AbsolutePath.EndsWith(".otf")));
return matchingResources.Select(x => GetAssetUri(x.absolutePath, x.assembly));
return matchingResources;
}
/// <summary>
/// Returns a <see cref="Uri"/> for a font asset that follows the resm scheme
/// </summary>
/// <param name="absolutePath"></param>
/// <param name="assembly"></param>
/// <returns></returns>
private static Uri GetAssetUri(string absolutePath, Assembly assembly)
{
return new Uri("resm:" + absolutePath + "?assembly=" + assembly.GetName().Name, UriKind.RelativeOrAbsolute);
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="AvaloniaXamlLoader.cs" />
<Compile Include="Converters\AvaloniaUriTypeConverter.cs" />
<Compile Include="Converters\MemberSelectorTypeConverter.cs" />
<Compile Include="Converters\ParseTypeConverter.cs" />
<Compile Include="Converters\SetterValueTypeConverter.cs" />
@ -18,6 +19,7 @@
<Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
<Compile Include="MarkupExtensions\StyleIncludeExtension.cs" />
<Compile Include="Parsers\PropertyParser.cs" />
<Compile Include="PortableXaml\AvaloniaResourceXamlInfo.cs" />
<Compile Include="PortableXaml\AvaloniaXamlContext.cs" />
<Compile Include="PortableXaml\AttributeExtensions.cs" />
<Compile Include="PortableXaml\AvaloniaMemberAttributeProvider.cs" />

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

@ -37,7 +37,8 @@ namespace Avalonia.Markup.Xaml
{ typeof(Selector), typeof(SelectorTypeConverter) },
{ typeof(TimeSpan), typeof(TimeSpanTypeConverter) },
{ typeof(WindowIcon), typeof(IconTypeConverter) },
{ typeof(CultureInfo), typeof(CultureInfoConverter)}
{ typeof(CultureInfo), typeof(CultureInfoConverter)},
{ typeof(Uri), typeof(AvaloniaUriTypeConverter)}
};
/// <summary>

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

@ -10,6 +10,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
namespace Avalonia.Markup.Xaml
@ -85,7 +87,7 @@ namespace Avalonia.Markup.Xaml
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
}
foreach (var uri in GetUrisFor(type))
foreach (var uri in GetUrisFor(assetLocator, type))
{
if (assetLocator.Exists(uri))
{
@ -134,18 +136,14 @@ namespace Avalonia.Markup.Xaml
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, uri);
return Load(stream, asset.assembly, rootInstance, absoluteUri);
}
catch (Exception e)
{
var uriString = uri.ToString();
if (!uri.IsAbsoluteUri)
{
uriString = new Uri(baseUri, uri).AbsoluteUri;
}
throw new XamlLoadException("Error loading xaml at " + uriString + ": " + e.Message, e);
throw new XamlLoadException("Error loading xaml at " + absoluteUri + ": " + e.Message, e);
}
}
}
@ -221,15 +219,37 @@ namespace Avalonia.Markup.Xaml
return LoadFromReader(reader, null);
}
private static readonly DataContractSerializer s_xamlInfoSerializer =
new DataContractSerializer(typeof(AvaloniaResourceXamlInfo));
/// <summary>
/// Gets the URI for a type.
/// </summary>
/// <param name="assetLocator"></param>
/// <param name="type">The type.</param>
/// <returns>The URI.</returns>
private static IEnumerable<Uri> GetUrisFor(Type type)
private static IEnumerable<Uri> GetUrisFor(IAssetLoader assetLocator, Type type)
{
var asm = type.GetTypeInfo().Assembly.GetName().Name;
var xamlInfoUri = new Uri($"res:asm:{asm}/!AvaloniaResourceXamlInfo");
var typeName = type.FullName;
if (typeName == null)
throw new ArgumentException("Type doesn't have a FullName");
if (assetLocator.Exists(xamlInfoUri))
{
using (var xamlInfoStream = assetLocator.Open(xamlInfoUri))
{
var xamlInfo = (AvaloniaResourceXamlInfo)s_xamlInfoSerializer.ReadObject(xamlInfoStream);
if (xamlInfo.ClassToResourcePathIndex.TryGetValue(typeName, out var rv) == true)
{
yield return new Uri($"res:asm:{asm}{rv}");
yield break;
}
}
}
yield return new Uri("resm:" + typeName + ".xaml?assembly=" + asm);
yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm);
}

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

@ -0,0 +1,25 @@
using System;
using System.ComponentModel;
using System.Globalization;
namespace Avalonia.Markup.Xaml.Converters
{
public class AvaloniaUriTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var s = value as string;
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);
}
}
}

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

@ -20,18 +20,16 @@ namespace Avalonia.Markup.Xaml.Converters
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var uri = new Uri((string)value, UriKind.RelativeOrAbsolute);
var scheme = uri.IsAbsoluteUri ? uri.Scheme : "file";
var s = (string)value;
var uri = s.StartsWith("/")
? new Uri(s, UriKind.Relative)
: new Uri(s, UriKind.RelativeOrAbsolute);
switch (scheme)
{
case "file":
return new Bitmap((string)value);
if(uri.IsAbsoluteUri && uri.IsFile)
return new Bitmap(uri.LocalPath);
default:
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new Bitmap(assets.Open(uri, context.GetBaseUri()));
}
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new Bitmap(assets.Open(uri, context.GetBaseUri()));
}
}
}

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

@ -36,20 +36,16 @@ namespace Avalonia.Markup.Xaml.Converters
throw new NotSupportedException();
}
private WindowIcon CreateIconFromPath(ITypeDescriptorContext context, string path)
private WindowIcon CreateIconFromPath(ITypeDescriptorContext context, string s)
{
var uri = new Uri(path, UriKind.RelativeOrAbsolute);
var scheme = uri.IsAbsoluteUri ? uri.Scheme : "file";
switch (scheme)
{
case "file":
return new WindowIcon(path);
default:
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new WindowIcon(assets.Open(uri, context.GetBaseUri()));
}
var uri = s.StartsWith("/")
? new Uri(s, UriKind.Relative)
: new Uri(s, UriKind.RelativeOrAbsolute);
if(uri.IsAbsoluteUri && uri.IsFile)
return new WindowIcon(uri.LocalPath);
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new WindowIcon(assets.Open(uri, context.GetBaseUri()));
}
}
}

12
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs

@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Avalonia.Markup.Xaml.PortableXaml
{
[DataContract]
class AvaloniaResourceXamlInfo
{
[DataMember]
public Dictionary<string, string> ClassToResourcePathIndex { get; set; } = new Dictionary<string, string>();
}
}

172
src/Shared/PlatformSupport/AssetLoader.cs

@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using Avalonia.Platform;
using Avalonia.Utilities;
namespace Avalonia.Shared.PlatformSupport
{
@ -15,6 +16,7 @@ namespace Avalonia.Shared.PlatformSupport
/// </summary>
public class AssetLoader : IAssetLoader
{
private const string AvaloniaResourceName = "!AvaloniaResources";
private static readonly Dictionary<string, AssemblyDescriptor> AssemblyNameCache
= new Dictionary<string, AssemblyDescriptor>();
@ -99,19 +101,50 @@ namespace Avalonia.Shared.PlatformSupport
/// Gets all assets of a folder and subfolders that match specified uri.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
public IEnumerable<(string absolutePath, Assembly assembly)> GetAssets(Uri uri)
public IEnumerable<Uri> GetAssets(Uri uri, Uri baseUri)
{
var assembly = GetAssembly(uri);
if (uri.IsAbsoluteUri && uri.Scheme == "resm")
{
var assembly = GetAssembly(uri);
return assembly?.Resources.Where(x => x.Key.Contains(uri.AbsolutePath))
.Select(x =>new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ??
Enumerable.Empty<Uri>();
}
return assembly?.Resources.Where(x => x.Key.Contains(uri.AbsolutePath))
.Select(x => (x.Key, x.Value.Assembly)) ??
Enumerable.Empty<(string AbsolutePath, Assembly Assembly)>();
uri = EnsureAbsolute(uri, baseUri);
if (uri.Scheme == "res")
{
var (asm, path) = GetResAsmAndPath(uri);
if (asm?.AvaloniaResources == null)
return Enumerable.Empty<Uri>();
path = path.TrimEnd('/') + '/';
return asm.AvaloniaResources.Where(r => r.Key.StartsWith(path))
.Select(x => new Uri($"res:asm:{asm.Name}{x.Key}"));
}
return Enumerable.Empty<Uri>();
}
private IAssetDescriptor GetAsset(Uri uri, Uri baseUri)
private Uri EnsureAbsolute(Uri uri, Uri baseUri)
{
if (!uri.IsAbsoluteUri || uri.Scheme == "resm")
if (uri.IsAbsoluteUri)
return uri;
if(baseUri == null)
throw new ArgumentException($"Relative uri {uri} without base url");
if (!baseUri.IsAbsoluteUri)
throw new ArgumentException($"Base uri {baseUri} is relative");
if (baseUri.Scheme == "resm")
throw new ArgumentException(
$"Relative uris for 'resm' scheme aren't supported; {baseUri} uses resm");
return new Uri(baseUri, uri);
}
private IAssetDescriptor GetAsset(Uri uri, Uri baseUri)
{
if (uri.IsAbsoluteUri && uri.Scheme == "resm")
{
var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultAssembly;
@ -128,9 +161,44 @@ namespace Avalonia.Shared.PlatformSupport
asm.Resources.TryGetValue(resourceKey, out rv);
return rv;
}
throw new ArgumentException($"Invalid uri, see https://github.com/AvaloniaUI/Avalonia/issues/282#issuecomment-166982104", nameof(uri));
uri = EnsureAbsolute(uri, baseUri);
if (uri.Scheme == "res")
{
var (asm, path) = GetResAsmAndPath(uri);
if (asm.AvaloniaResources == null)
return null;
asm.AvaloniaResources.TryGetValue(path, out var desc);
return desc;
}
throw new ArgumentException($"Unsupported url type: " + uri.Scheme, nameof(uri));
}
private (AssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri)
{
string path = null, asmPart = null;
if (!uri.AbsolutePath.StartsWith("asm:"))
path = uri.AbsolutePath;
else
{
var sp = uri.AbsolutePath.Split(new[] {'/'}, 2);
asmPart = sp[0].Substring(4);
path = '/' + sp[1];
}
var asm = (asmPart == null ? null : GetAssembly(asmPart)) ?? _defaultAssembly;
if (asm == null)
{
throw new ArgumentException(
"No default assembly, entry assembly or explicit assembly specified; " +
"don't know where to look up for the resource, try specifying assembly explicitly.");
}
return (asm, path);
}
private AssemblyDescriptor GetAssembly(Uri uri)
{
if (uri != null)
@ -213,6 +281,80 @@ namespace Avalonia.Shared.PlatformSupport
public Assembly Assembly => _asm;
}
private class AvaloniaResourceDescriptor : IAssetDescriptor
{
private readonly int _offset;
private readonly int _length;
public Assembly Assembly { get; }
public AvaloniaResourceDescriptor(Assembly asm, int offset, int length)
{
_offset = offset;
_length = length;
Assembly = asm;
}
public Stream GetStream()
{
return new SlicedStream(Assembly.GetManifestResourceStream(AvaloniaResourceName), _offset, _length);
}
}
class SlicedStream : Stream
{
private readonly Stream _baseStream;
private readonly int _from;
public SlicedStream(Stream baseStream, int from, int length)
{
Length = length;
_baseStream = baseStream;
_from = from;
_baseStream.Position = from;
}
public override void Flush()
{
}
public override int Read(byte[] buffer, int offset, int count)
{
return _baseStream.Read(buffer, offset, (int)Math.Min(count, Length - Position));
}
public override long Seek(long offset, SeekOrigin origin)
{
if (origin == SeekOrigin.Begin)
Position = offset;
if (origin == SeekOrigin.End)
Position = _from + Length + offset;
if (origin == SeekOrigin.Current)
Position = Position + offset;
return Position;
}
public override void SetLength(long value) => throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
public override bool CanRead => true;
public override bool CanSeek => _baseStream.CanRead;
public override bool CanWrite => false;
public override long Length { get; }
public override long Position
{
get => _baseStream.Position - _from;
set => _baseStream.Position = value + _from;
}
protected override void Dispose(bool disposing)
{
if (disposing)
_baseStream.Dispose();
}
public override void Close() => _baseStream.Close();
}
private class AssemblyDescriptor
{
@ -225,11 +367,25 @@ namespace Avalonia.Shared.PlatformSupport
Resources = assembly.GetManifestResourceNames()
.ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n));
Name = assembly.GetName().Name;
using (var resources = assembly.GetManifestResourceStream(AvaloniaResourceName))
{
if (resources != null)
{
Resources.Remove(AvaloniaResourceName);
var indexLength = new BinaryReader(resources).ReadInt32();
var index = AvaloniaResourcesIndexReaderWriter.Read(new SlicedStream(resources, 4, indexLength));
var baseOffset = indexLength + 4;
AvaloniaResources = index.ToDictionary(r => "/" + r.Path.TrimStart('/'), r => (IAssetDescriptor)
new AvaloniaResourceDescriptor(assembly, baseOffset + r.Offset, r.Size));
}
}
}
}
public Assembly Assembly { get; }
public Dictionary<string, IAssetDescriptor> Resources { get; }
public Dictionary<string, IAssetDescriptor> AvaloniaResources { get; }
public string Name { get; }
}
}

5
tests/Avalonia.UnitTests/MockAssetLoader.cs

@ -32,10 +32,9 @@ namespace Avalonia.UnitTests
return (Open(uri, baseUri), (Assembly)null);
}
public IEnumerable<(string absolutePath, Assembly assembly)> GetAssets(Uri uri)
public IEnumerable<Uri> GetAssets(Uri uri, Uri baseUri)
{
return _assets.Keys.Where(x => x.AbsolutePath.Contains(uri.AbsolutePath))
.Select(x => (x.AbsolutePath, Assembly.GetEntryAssembly()));
return _assets.Keys.Where(x => x.AbsolutePath.Contains(uri.AbsolutePath));
}
public void SetDefaultAssembly(Assembly asm)

Loading…
Cancel
Save