Browse Source

Initial remote XAML previewer implementation

pull/1105/head
Nikita Tsukanov 8 years ago
parent
commit
9a1348b37b
  1. 92
      Avalonia.sln
  2. 1
      Avalonia.sln.DotSettings
  3. 22
      samples/ControlCatalog.NetCore/Program.cs
  4. 6
      samples/Previewer/App.xaml
  5. 14
      samples/Previewer/App.xaml.cs
  6. 19
      samples/Previewer/Center.cs
  7. 12
      samples/Previewer/MainWindow.xaml
  8. 86
      samples/Previewer/MainWindow.xaml.cs
  9. 27
      samples/Previewer/Previewer.csproj
  10. 13
      samples/Previewer/Program.cs
  11. 2
      samples/RemoteTest/RemoteTest.csproj
  12. 2
      src/Avalonia.Controls/Design.cs
  13. 14
      src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
  14. 2
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  15. 10
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  16. 76
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  17. 60
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  18. 4
      src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
  19. 19
      src/Avalonia.Remote.Protocol/BsonStreamTransport.cs
  20. 17
      src/Avalonia.Remote.Protocol/DesignMessages.cs
  21. 20
      src/Avalonia.Remote.Protocol/EventStash.cs
  22. 4
      src/Avalonia.Remote.Protocol/ITransport.cs
  23. 16
      src/Avalonia.Remote.Protocol/TcpTransportBase.cs
  24. 12
      src/Avalonia.Remote.Protocol/TransportConnectionWrapper.cs
  25. 7
      src/Avalonia.Remote.Protocol/ViewportMessages.cs
  26. 24
      src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj
  27. 7
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  28. 2
      src/Linux/Avalonia.LinuxFramebuffer/Mice.cs
  29. 25
      src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
  30. 35
      src/tools/Avalonia.Designer.HostApp/DetachableTransportConnection.cs
  31. 91
      src/tools/Avalonia.Designer.HostApp/PreviewerWindowImpl.cs
  32. 66
      src/tools/Avalonia.Designer.HostApp/PreviewerWindowingPlatform.cs
  33. 173
      src/tools/Avalonia.Designer.HostApp/Program.cs
  34. 136
      src/tools/Avalonia.Designer.HostApp/Stubs.cs

92
Avalonia.sln

@ -1,4 +1,4 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1
@ -189,6 +189,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Remote.Protocol",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemoteTest", "samples\RemoteTest\RemoteTest.csproj", "{E2999E4A-9086-401F-898C-AEB0AD38E676}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{4ED8B739-6F4E-4CD4-B993-545E6B5CE637}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Designer.HostApp", "src\tools\Avalonia.Designer.HostApp\Avalonia.Designer.HostApp.csproj", "{050CC912-FF49-4A8B-B534-9544017446DD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Previewer", "samples\Previewer\Previewer.csproj", "{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -2598,6 +2604,86 @@ Global
{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Mono.Build.0 = Release|Any CPU
{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|x86.ActiveCfg = Release|Any CPU
{E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|x86.Build.0 = Release|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Mono.ActiveCfg = Ad-Hoc|Mono
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Mono.Build.0 = Ad-Hoc|Mono
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|x86.ActiveCfg = Ad-Hoc|x86
{050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|x86.Build.0 = Ad-Hoc|x86
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhone.Build.0 = AppStore|iPhone
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Mono.ActiveCfg = AppStore|Mono
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Mono.Build.0 = AppStore|Mono
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|x86.ActiveCfg = AppStore|x86
{050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|x86.Build.0 = AppStore|x86
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhone.ActiveCfg = Debug|iPhone
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhone.Build.0 = Debug|iPhone
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Mono.ActiveCfg = Debug|Mono
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Mono.Build.0 = Debug|Mono
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|x86.ActiveCfg = Debug|x86
{050CC912-FF49-4A8B-B534-9544017446DD}.Debug|x86.Build.0 = Debug|x86
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|Any CPU.Build.0 = Release|Any CPU
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhone.ActiveCfg = Release|iPhone
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhone.Build.0 = Release|iPhone
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|Mono.ActiveCfg = Release|Mono
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|Mono.Build.0 = Release|Mono
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|x86.ActiveCfg = Release|x86
{050CC912-FF49-4A8B-B534-9544017446DD}.Release|x86.Build.0 = Release|x86
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Mono.ActiveCfg = Ad-Hoc|Mono
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Mono.Build.0 = Ad-Hoc|Mono
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|x86.ActiveCfg = Ad-Hoc|x86
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|x86.Build.0 = Ad-Hoc|x86
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhone.Build.0 = AppStore|iPhone
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Mono.ActiveCfg = AppStore|Mono
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Mono.Build.0 = AppStore|Mono
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|x86.ActiveCfg = AppStore|x86
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|x86.Build.0 = AppStore|x86
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhone.ActiveCfg = Debug|iPhone
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhone.Build.0 = Debug|iPhone
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Mono.ActiveCfg = Debug|Mono
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Mono.Build.0 = Debug|Mono
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|x86.ActiveCfg = Debug|x86
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|x86.Build.0 = Debug|x86
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Any CPU.Build.0 = Release|Any CPU
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.ActiveCfg = Release|iPhone
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.Build.0 = Release|iPhone
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Mono.ActiveCfg = Release|Mono
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Mono.Build.0 = Release|Mono
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|x86.ActiveCfg = Release|x86
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2656,8 +2742,12 @@ Global
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{E2999E4A-9086-401F-898C-AEB0AD38E676} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{050CC912-FF49-4A8B-B534-9544017446DD} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1E8CA5AA-707A-4C57-A682-D265A49E10C3}
{050CC912-FF49-4A8B-B534-9544017446DD} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
EndGlobal

1
Avalonia.sln.DotSettings

@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Examl/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=3E53A01A_002DB331_002D47F3_002DB828_002D4A5717E77A24_002Fd_003Aglass/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String>

22
samples/ControlCatalog.NetCore/Program.cs

@ -1,6 +1,7 @@
using System;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
namespace ControlCatalog.NetCore
{
@ -8,17 +9,22 @@ namespace ControlCatalog.NetCore
{
static void Main(string[] args)
{
if (args.Contains("--fbdev")) AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl =>
{
tl.Content = new MainView();
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
});
if (args.Contains("--fbdev"))
AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl =>
{
tl.Content = new MainView();
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
});
else
AppBuilder.Configure<App>()
.UsePlatformDetect()
.Start<MainWindow>();
BuildAvaloniaApp().Start<MainWindow>();
}
/// <summary>
/// This method is needed for IDE previewer infrastructure
/// </summary>
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>().UsePlatformDetect();
static void ConsoleSilencer()
{
Console.CursorVisible = false;

6
samples/Previewer/App.xaml

@ -0,0 +1,6 @@
<Application xmlns="https://github.com/avaloniaui">
<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"/>
</Application.Styles>
</Application>

14
samples/Previewer/App.xaml.cs

@ -0,0 +1,14 @@
using Avalonia;
using Avalonia.Markup.Xaml;
namespace Previewer
{
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
}
}

19
samples/Previewer/Center.cs

@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Controls;
namespace Previewer
{
public class Center : Decorator
{
protected override Size ArrangeOverride(Size finalSize)
{
if (Child != null)
{
var desired = Child.DesiredSize;
Child.Arrange(new Rect((finalSize.Width - desired.Width) / 2, (finalSize.Height - desired.Height) / 2,
desired.Width, desired.Height));
}
return finalSize;
}
}
}

12
samples/Previewer/MainWindow.xaml

@ -0,0 +1,12 @@
<Window xmlns="https://github.com/avaloniaui" Width="600" Height="500"
Title="Previewer">
<Grid RowDefinitions="0.5*,200">
<ScrollViewer Name="Remote"/>
<ScrollViewer Name="ErrorsContainer" Background="#ffe0e0">
<TextBlock Name="Errors"/>
</ScrollViewer>
<TextBox Grid.Row="1" AcceptsReturn="True" Name="Xaml"/>
</Grid>
</Window>

86
samples/Previewer/MainWindow.xaml.cs

@ -0,0 +1,86 @@
using System;
using System.Net;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Remote;
using Avalonia.Markup.Xaml;
using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Designer;
using Avalonia.Remote.Protocol.Viewport;
using Avalonia.Threading;
namespace Previewer
{
public class MainWindow : Window
{
private const string InitialXaml = @"<Window xmlns=""https://github.com/avaloniaui"" Width=""600"" Height=""500"">
<TextBlock>Hello world!</TextBlock>
</Window>";
private IAvaloniaRemoteTransportConnection _connection;
private Control _errorsContainer;
private TextBlock _errors;
private RemoteWidget _remote;
public MainWindow()
{
this.InitializeComponent();
var tb = this.FindControl<TextBox>("Xaml");
tb.Text = InitialXaml;
var scroll = this.FindControl<ScrollViewer>("Remote");
var rem = new Center();
scroll.Content = rem;
_errorsContainer = this.FindControl<Control>("ErrorsContainer");
_errors = this.FindControl<TextBlock>("Errors");
tb.GetObservable(TextBox.TextProperty).Subscribe(text => _connection?.Send(new UpdateXamlMessage
{
Xaml = text
}));
new BsonTcpTransport().Listen(IPAddress.Loopback, 25000, t =>
{
Dispatcher.UIThread.InvokeAsync(() =>
{
if (_connection != null)
{
_connection.Dispose();
_connection.OnMessage -= OnMessage;
}
_connection = t;
rem.Child = _remote = new RemoteWidget(t);
t.Send(new UpdateXamlMessage
{
Xaml = tb.Text
});
t.OnMessage += OnMessage;
});
});
Title = "Listening on 127.0.0.1:25000";
}
private void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
if (transport != _connection)
return;
if (obj is UpdateXamlResultMessage result)
{
_errorsContainer.IsVisible = result.Error != null;
_errors.Text = result.Error ?? "";
}
if (obj is RequestViewportResizeMessage resize)
{
_remote.Width = Math.Min(4096, Math.Max(resize.Width, 1));
_remote.Height = Math.Min(4096, Math.Max(resize.Height, 1));
}
});
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

27
samples/Previewer/Previewer.csproj

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<EmbeddedResource Include="**\*.xaml" />
<ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.HtmlRenderer\Avalonia.HtmlRenderer.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
</ItemGroup>
</Project>

13
samples/Previewer/Program.cs

@ -0,0 +1,13 @@
using System;
using Avalonia;
namespace Previewer
{
class Program
{
static void Main(string[] args)
{
AppBuilder.Configure<App>().UsePlatformDetect().Start<MainWindow>();
}
}
}

2
samples/RemoteTest/RemoteTest.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

2
src/Avalonia.Controls/Design.cs

@ -64,7 +64,7 @@ namespace Avalonia.Controls
return rv;
}
internal static void ApplyDesignerProperties(Control target, Control source)
public static void ApplyDesignModeProperties(Control target, Control source)
{
if (source.IsSet(WidthProperty))
target.Width = source.GetValue(WidthProperty);

14
src/Linux/Avalonia.LinuxFramebuffer/PlatformThreadingInterface.cs → src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs

@ -1,19 +1,16 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Platform;
using Avalonia.Rendering;
namespace Avalonia.LinuxFramebuffer
namespace Avalonia.Controls.Platform
{
class PlatformThreadingInterface : IPlatformThreadingInterface, IRenderLoop
public class InternalPlatformThreadingInterface : IPlatformThreadingInterface, IRenderLoop
{
public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface();
public PlatformThreadingInterface()
public InternalPlatformThreadingInterface()
{
TlsCurrentThreadIsLoopThread = true;
StartTimer(new TimeSpan(0, 0, 0, 0, 66), () => Tick?.Invoke(this, new EventArgs()));
@ -36,7 +33,7 @@ namespace Avalonia.LinuxFramebuffer
while (true)
{
Action item;
lock(_actions)
lock (_actions)
if (_actions.Count == 0)
break;
else
@ -66,6 +63,7 @@ namespace Avalonia.LinuxFramebuffer
_timer = timer;
_handle = GCHandle.Alloc(_timer);
}
public void Dispose()
{
_handle.Free();
@ -109,4 +107,4 @@ namespace Avalonia.LinuxFramebuffer
public event EventHandler<EventArgs> Tick;
}
}
}

2
src/Avalonia.Controls/Remote/RemoteWidget.cs

@ -18,7 +18,7 @@ namespace Avalonia.Controls.Remote
public RemoteWidget(IAvaloniaRemoteTransportConnection connection)
{
_connection = connection;
_connection.OnMessage += msg => Dispatcher.UIThread.InvokeAsync(() => OnMessage(msg));
_connection.OnMessage += (t, msg) => Dispatcher.UIThread.InvokeAsync(() => OnMessage(msg));
_connection.Send(new ClientSupportedPixelFormatsMessage
{
Formats = new[]

10
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -36,7 +36,7 @@ namespace Avalonia.Controls.Remote.Server
_transport.OnMessage += OnMessage;
}
private void OnMessage(object obj)
protected virtual void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
{
lock (_lock)
{
@ -88,6 +88,12 @@ namespace Avalonia.Controls.Remote.Server
}
}
protected void SetDpi(Vector dpi)
{
_dpi = dpi;
RenderIfNeeded();
}
protected virtual Size Measure(Size constaint)
{
var l = (ILayoutable) InputRoot;
@ -131,7 +137,7 @@ namespace Avalonia.Controls.Remote.Server
return _framebuffer;
}
void RenderIfNeeded()
protected void RenderIfNeeded()
{
lock (_lock)
{

76
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -0,0 +1,76 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Markup.Xaml;
using Avalonia.Styling;
namespace Avalonia.DesignerSupport
{
public class DesignWindowLoader
{
public static Window LoadDesignerWindow(string xaml, string assemblyPath)
{
Window window;
Control control;
using (PlatformManager.DesignerMode())
{
var loader = new AvaloniaXamlLoader();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
Uri baseUri = null;
if (assemblyPath != null)
{
//Fabricate fake Uri
baseUri =
new Uri("resm:Fake.xaml?assembly=" + Path.GetFileNameWithoutExtension(assemblyPath));
}
var loaded = loader.Load(stream, null, baseUri);
var styles = loaded as Styles;
if (styles != null)
{
var substitute = Design.GetPreviewWith(styles) ??
styles.Select(Design.GetPreviewWith).FirstOrDefault(s => s != null);
if (substitute != null)
{
substitute.Styles.AddRange(styles);
control = substitute;
}
else
control = new StackPanel
{
Children =
{
new TextBlock {Text = "Styles can't be previewed without Design.PreviewWith. Add"},
new TextBlock {Text = "<Design.PreviewWith>"},
new TextBlock {Text = " <Border Padding=20><!-- YOUR CONTROL FOR PREVIEW HERE--></Border>"},
new TextBlock {Text = "<Design.PreviewWith>"},
new TextBlock {Text = "before setters in your first Style"}
}
};
}
if (loaded is Application)
control = new TextBlock {Text = "Application can't be previewed in design view"};
else
control = (Control) loaded;
window = control as Window;
if (window == null)
{
window = new Window() {Content = (Control)control};
}
if (!window.IsSet(Window.SizeToContentProperty))
window.SizeToContent = SizeToContent.WidthAndHeight;
}
window.Show();
Design.ApplyDesignModeProperties(window, control);
return window;
}
}
}

60
src/Avalonia.DesignerSupport/DesignerAssist.cs

@ -86,67 +86,11 @@ namespace Avalonia.DesignerSupport
private static void UpdateXaml2(Dictionary<string, object> dic)
{
var xamlInfo = new DesignerApiXamlFileInfo(dic);
Window window;
Control control;
using (PlatformManager.DesignerMode())
{
var loader = new AvaloniaXamlLoader();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xamlInfo.Xaml));
Uri baseUri = null;
if (xamlInfo.AssemblyPath != null)
{
//Fabricate fake Uri
baseUri =
new Uri("resm:Fake.xaml?assembly=" + Path.GetFileNameWithoutExtension(xamlInfo.AssemblyPath));
}
var loaded = loader.Load(stream, null, baseUri);
var styles = loaded as Styles;
if (styles != null)
{
var substitute = Design.GetPreviewWith(styles) ??
styles.Select(Design.GetPreviewWith).FirstOrDefault(s => s != null);
if (substitute != null)
{
substitute.Styles.AddRange(styles);
control = substitute;
}
else
control = new StackPanel
{
Children =
{
new TextBlock {Text = "Styles can't be previewed without Design.PreviewWith. Add"},
new TextBlock {Text = "<Design.PreviewWith>"},
new TextBlock {Text = " <Border Padding=20><!-- YOUR CONTROL FOR PREVIEW HERE--></Border>"},
new TextBlock {Text = "<Design.PreviewWith>"},
new TextBlock {Text = "before setters in your first Style"}
}
};
}
if (loaded is Application)
control = new TextBlock {Text = "Application can't be previewed in design view"};
else
control = (Control) loaded;
window = control as Window;
if (window == null)
{
window = new Window() {Content = (Control)control};
}
if (!window.IsSet(Window.SizeToContentProperty))
window.SizeToContent = SizeToContent.WidthAndHeight;
}
Window window = DesignWindowLoader.LoadDesignerWindow(xamlInfo.Xaml, xamlInfo.AssemblyPath);
s_currentWindow?.Close();
s_currentWindow = window;
window.Show();
Design.ApplyDesignerProperties(window, control);
// ReSharper disable once PossibleNullReferenceException
// Always not null at this point
Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle);

4
src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj

@ -10,7 +10,7 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Reference Include="System.Runtime"/>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
@ -18,4 +18,4 @@
</ItemGroup>
<Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<Import Project="..\..\build\Rx.props" />
</Project>
</Project>

19
src/Avalonia.Remote.Protocol/BsonStreamTransport.cs

@ -8,7 +8,7 @@ using Newtonsoft.Json.Bson;
namespace Avalonia.Remote.Protocol
{
public class BsonStreamTransportConnection : IAvaloniaRemoteTransportConnection
class BsonStreamTransportConnection : IAvaloniaRemoteTransportConnection
{
private readonly IMessageTypeResolver _resolver;
private readonly Stream _inputStream;
@ -79,17 +79,8 @@ namespace Avalonia.Remote.Protocol
var guid = new Guid(guidBytes);
var buffer = new byte[length];
await ReadExact(buffer).ConfigureAwait(false);
if (Environment.GetEnvironmentVariable("WTF") == "WTF")
{
using (var f = System.IO.File.Create("/tmp/wtf2.bin"))
{
f.Write(infoBlock, 0, infoBlock.Length);
f.Write(buffer, 0, buffer.Length);
}
}
var message = Serializer.Deserialize(new BsonReader(new MemoryStream(buffer)), _resolver.GetByGuid(guid));
OnMessage?.Invoke(message);
OnMessage?.Invoke(this, message);
}
}
catch (Exception e)
@ -150,11 +141,11 @@ namespace Avalonia.Remote.Protocol
var cancel = e as OperationCanceledException;
if (cancel?.CancellationToken == _cancel)
return;
OnException?.Invoke(e);
OnException?.Invoke(this, e);
}
public event Action<object> OnMessage;
public event Action<Exception> OnException;
public event Action<IAvaloniaRemoteTransportConnection, object> OnMessage;
public event Action<IAvaloniaRemoteTransportConnection, Exception> OnException;
}
}

17
src/Avalonia.Remote.Protocol/DesignMessages.cs

@ -0,0 +1,17 @@
namespace Avalonia.Remote.Protocol.Designer
{
[AvaloniaRemoteMessageGuid("9AEC9A2E-6315-4066-B4BA-E9A9EFD0F8CC")]
public class UpdateXamlMessage
{
public string Xaml { get; set; }
public string AssemblyPath { get; set; }
}
[AvaloniaRemoteMessageGuid("B7A70093-0C5D-47FD-9261-22086D43A2E2")]
public class UpdateXamlResultMessage
{
public string Error { get; set; }
}
}

20
src/Avalonia.Remote.Protocol/EventStash.cs

@ -3,18 +3,20 @@ using System.Collections.Generic;
namespace Avalonia.Remote.Protocol
{
public class EventStash<T>
class EventStash<T>
{
private readonly IAvaloniaRemoteTransportConnection _transport;
private readonly Action<Exception> _exceptionHandler;
private List<T> _stash;
private Action<T> _delegate;
private Action<IAvaloniaRemoteTransportConnection, T> _delegate;
public EventStash(Action<Exception> exceptionHandler = null)
public EventStash(IAvaloniaRemoteTransportConnection transport, Action<Exception> exceptionHandler = null)
{
_transport = transport;
_exceptionHandler = exceptionHandler;
}
public void Add(Action<T> handler)
public void Add(Action<IAvaloniaRemoteTransportConnection, T> handler)
{
List<T> stash;
lock (this)
@ -37,25 +39,25 @@ namespace Avalonia.Remote.Protocol
if (_exceptionHandler != null)
try
{
_delegate?.Invoke(m);
_delegate?.Invoke(_transport, m);
}
catch (Exception e)
{
_exceptionHandler(e);
}
else
_delegate?.Invoke(m);
_delegate?.Invoke(_transport, m);
}
}
public void Remove(Action<T> handler)
public void Remove(Action<IAvaloniaRemoteTransportConnection, T> handler)
{
lock (this)
_delegate -= handler;
}
public void Fire(T ev)
public void Fire(IAvaloniaRemoteTransportConnection transport, T ev)
{
if (_delegate == null)
{
@ -66,7 +68,7 @@ namespace Avalonia.Remote.Protocol
}
}
else
_delegate?.Invoke(ev);
_delegate?.Invoke(_transport, ev);
}
}
}

4
src/Avalonia.Remote.Protocol/ITransport.cs

@ -9,7 +9,7 @@ namespace Avalonia.Remote.Protocol
public interface IAvaloniaRemoteTransportConnection : IDisposable
{
Task Send(object data);
event Action<object> OnMessage;
event Action<Exception> OnException;
event Action<IAvaloniaRemoteTransportConnection, object> OnMessage;
event Action<IAvaloniaRemoteTransportConnection, Exception> OnException;
}
}

16
src/Avalonia.Remote.Protocol/TcpTransportBase.cs

@ -50,17 +50,11 @@ namespace Avalonia.Remote.Protocol
AcceptNew();
Task.Run(async () =>
{
try
{
var tcs = new TaskCompletionSource<int>();
var t = CreateTransport(_resolver, cl.GetStream(), () => tcs.TrySetResult(0));
cb(t);
await tcs.Task;
}
finally
{
cl.Dispose();
}
var tcs = new TaskCompletionSource<int>();
var t = CreateTransport(_resolver, cl.GetStream(), () => tcs.TrySetResult(0));
cb(t);
await tcs.Task;
});
}

12
src/Avalonia.Remote.Protocol/TransportConnectionWrapper.cs

@ -8,7 +8,7 @@ namespace Avalonia.Remote.Protocol
{
private readonly IAvaloniaRemoteTransportConnection _conn;
private EventStash<object> _onMessage;
private EventStash<Exception> _onException = new EventStash<Exception>();
private EventStash<Exception> _onException;
private Queue<SendOperation> _sendQueue = new Queue<SendOperation>();
private object _lock =new object();
@ -17,7 +17,8 @@ namespace Avalonia.Remote.Protocol
public TransportConnectionWrapper(IAvaloniaRemoteTransportConnection conn)
{
_conn = conn;
_onMessage = new EventStash<object>(_onException.Fire);
_onException = new EventStash<Exception>(this);
_onMessage = new EventStash<object>(this, e => _onException.Fire(this, e));
_conn.OnException +=_onException.Fire;
conn.OnMessage += _onMessage.Fire;
@ -58,8 +59,7 @@ namespace Avalonia.Remote.Protocol
{
wi.Tcs.TrySetException(e);
}
}
}
}
public Task Send(object data)
@ -86,13 +86,13 @@ namespace Avalonia.Remote.Protocol
return tcs.Task;
}
public event Action<object> OnMessage
public event Action<IAvaloniaRemoteTransportConnection, object> OnMessage
{
add => _onMessage.Add(value);
remove => _onMessage.Remove(value);
}
public event Action<Exception> OnException
public event Action<IAvaloniaRemoteTransportConnection, Exception> OnException
{
add => _onException.Add(value);
remove => _onException.Remove(value);

7
src/Avalonia.Remote.Protocol/ViewportMessages.cs

@ -30,6 +30,13 @@ namespace Avalonia.Remote.Protocol.Viewport
public double DpiY { get; set; }
}
[AvaloniaRemoteMessageGuid("9B47B3D8-61DF-4C38-ACD4-8C1BB72554AC")]
public class RequestViewportResizeMessage
{
public double Width { get; set; }
public double Height { get; set; }
}
[AvaloniaRemoteMessageGuid("63481025-7016-43FE-BADC-F2FD0F88609E")]
public class ClientSupportedPixelFormatsMessage
{

24
src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj

@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup>
</Project>

7
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -12,6 +12,7 @@ using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia;
using Avalonia.Controls.Platform;
namespace Avalonia.LinuxFramebuffer
{
@ -22,6 +23,7 @@ namespace Avalonia.LinuxFramebuffer
public static MouseDevice MouseDevice = new MouseDevice();
private static readonly Stopwatch St = Stopwatch.StartNew();
internal static uint Timestamp => (uint)St.ElapsedTicks;
public static InternalPlatformThreadingInterface Threading;
public static FramebufferToplevelImpl TopLevel;
LinuxFramebufferPlatform(string fbdev = null)
{
@ -31,12 +33,13 @@ namespace Avalonia.LinuxFramebuffer
void Initialize()
{
Threading = new InternalPlatformThreadingInterface();
AvaloniaLocator.CurrentMutable
.Bind<IStandardCursorFactory>().ToTransient<CursorFactoryStub>()
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
.Bind<IRenderLoop>().ToConstant(PlatformThreadingInterface.Instance);
.Bind<IPlatformThreadingInterface>().ToConstant(Threading)
.Bind<IRenderLoop>().ToConstant(Threading);
}
internal static TopLevel Initialize<T>(T builder, string fbdev = null) where T : AppBuilderBase<T>, new()

2
src/Linux/Avalonia.LinuxFramebuffer/Mice.cs

@ -55,7 +55,7 @@ namespace Avalonia.LinuxFramebuffer
if (!ev.HasValue)
break;
PlatformThreadingInterface.Instance.Send(() => ProcessEvent(dev, ev.Value));
LinuxFramebufferPlatform.Threading.Send(() => ProcessEvent(dev, ev.Value));
}
}
}

25
src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\Avalonia.DesignerSupport\DesignWindowLoader.cs"/>
<ProjectReference Include="..\..\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
<ProjectReference Include="..\..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\Avalonia.HtmlRenderer\Avalonia.HtmlRenderer.csproj" />
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\..\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<ProjectReference Include="..\..\..\samples\ControlCatalog.NetCore\ControlCatalog.NetCore.csproj" />
</ItemGroup>
</Project>

35
src/tools/Avalonia.Designer.HostApp/DetachableTransportConnection.cs

@ -0,0 +1,35 @@
using System;
using System.Threading.Tasks;
using Avalonia.Remote.Protocol;
namespace Avalonia.Designer.HostApp
{
class DetachableTransportConnection : IAvaloniaRemoteTransportConnection
{
private IAvaloniaRemoteTransportConnection _inner;
public DetachableTransportConnection(IAvaloniaRemoteTransportConnection inner)
{
_inner = inner;
_inner.OnMessage += FireOnMessage;
}
public void Dispose()
{
if (_inner != null)
_inner.OnMessage -= FireOnMessage;
_inner = null;
}
public void FireOnMessage(IAvaloniaRemoteTransportConnection transport, object obj) => OnMessage?.Invoke(transport, obj);
public Task Send(object data)
{
return _inner?.Send(data);
}
public event Action<IAvaloniaRemoteTransportConnection, object> OnMessage;
public event Action<IAvaloniaRemoteTransportConnection, Exception> OnException;
}
}

91
src/tools/Avalonia.Designer.HostApp/PreviewerWindowImpl.cs

@ -0,0 +1,91 @@
using System;
using System.Reactive.Disposables;
using Avalonia.Controls;
using Avalonia.Controls.Remote.Server;
using Avalonia.Platform;
using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Viewport;
using Avalonia.Threading;
namespace Avalonia.Designer.HostApp
{
public class PreviewerWindowImpl : RemoteServerTopLevelImpl, IWindowImpl, IEmbeddableWindowImpl
{
private readonly IAvaloniaRemoteTransportConnection _transport;
public PreviewerWindowImpl(IAvaloniaRemoteTransportConnection transport) : base(transport)
{
_transport = transport;
ClientSize = new Size(1, 1);
}
public void Show()
{
}
public void Hide()
{
}
public void BeginMoveDrag()
{
}
public void BeginResizeDrag(WindowEdge edge)
{
}
public Point Position { get; set; }
public Action<Point> PositionChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
public IPlatformHandle Handle { get; }
public WindowState WindowState { get; set; }
public Size MaxClientSize { get; } = new Size(4096, 4096);
public event Action LostFocus;
protected override void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
{
// In previewer mode we completely ignore client-side viewport size
if (obj is ClientViewportAllocatedMessage alloc)
{
Dispatcher.UIThread.InvokeAsync(() => SetDpi(new Vector(alloc.DpiX, alloc.DpiY)));
return;
}
base.OnMessage(transport, obj);
}
public void Resize(Size clientSize)
{
_transport.Send(new RequestViewportResizeMessage
{
Width = clientSize.Width,
Height = clientSize.Height
});
ClientSize = clientSize;
RenderIfNeeded();
}
public void Activate()
{
}
public void SetTitle(string title)
{
}
public IDisposable ShowDialog()
{
return Disposable.Empty;
}
public void SetSystemDecorations(bool enabled)
{
}
public void SetIcon(IWindowIconImpl icon)
{
}
}
}

66
src/tools/Avalonia.Designer.HostApp/PreviewerWindowingPlatform.cs

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Platform;
using Avalonia.Remote.Protocol;
using Avalonia.Rendering;
namespace Avalonia.Designer.HostApp
{
class PreviewerWindowingPlatform : IWindowingPlatform, IPlatformSettings
{
static readonly IKeyboardDevice Keyboard = new KeyboardDevice();
private static IAvaloniaRemoteTransportConnection s_transport;
private static DetachableTransportConnection s_lastWindowTransport;
private static PreviewerWindowImpl s_lastWindow;
public static List<object> PreFlightMessages = new List<object>();
public IWindowImpl CreateWindow() => new WindowStub();
public IEmbeddableWindowImpl CreateEmbeddableWindow()
{
if (s_lastWindow != null)
{
s_lastWindowTransport.Dispose();
try
{
s_lastWindow.Dispose();
}
catch
{
//Ignore
}
}
s_lastWindow =
new PreviewerWindowImpl(s_lastWindowTransport = new DetachableTransportConnection(s_transport));
foreach (var pf in PreFlightMessages)
s_lastWindowTransport.FireOnMessage(s_lastWindowTransport, pf);
return s_lastWindow;
}
public IPopupImpl CreatePopup() => new WindowStub();
public static void Initialize(IAvaloniaRemoteTransportConnection transport)
{
s_transport = transport;
var instance = new PreviewerWindowingPlatform();
var threading = new InternalPlatformThreadingInterface();
AvaloniaLocator.CurrentMutable
.Bind<IClipboard>().ToSingleton<ClipboardStub>()
.Bind<IStandardCursorFactory>().ToSingleton<CursorFactoryStub>()
.Bind<IKeyboardDevice>().ToConstant(Keyboard)
.Bind<IPlatformSettings>().ToConstant(instance)
.Bind<IPlatformThreadingInterface>().ToConstant(threading)
.Bind<IRenderLoop>().ToConstant(threading)
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsStub>()
.Bind<IWindowingPlatform>().ToConstant(instance)
.Bind<IPlatformIconLoader>().ToSingleton<IconLoaderStub>();
}
public Size DoubleClickSize { get; } = new Size(2, 2);
public TimeSpan DoubleClickTime { get; } = TimeSpan.FromMilliseconds(500);
}
}

173
src/tools/Avalonia.Designer.HostApp/Program.cs

@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.DesignerSupport;
using Avalonia.Input;
using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Designer;
using Avalonia.Remote.Protocol.Viewport;
using Avalonia.Threading;
namespace Avalonia.Designer.HostApp
{
class Program
{
private static ClientSupportedPixelFormatsMessage s_supportedPixelFormats;
private static ClientViewportAllocatedMessage s_viewportAllocatedMessage;
private static IAvaloniaRemoteTransportConnection s_transport;
class CommandLineArgs
{
public string AppPath { get; set; }
public Uri Transport { get; set; }
}
static Exception Die(string error)
{
if (error != null)
{
Console.Error.WriteLine(error);
Console.Error.Flush();
}
Environment.Exit(1);
return new Exception("APPEXIT");
}
static Exception PrintUsage()
{
Console.Error.WriteLine("Usage: --transport transport_spec app");
Console.Error.WriteLine();
Console.Error.WriteLine("Example: --transport tcp-bson://127.0.0.1:30243/ MyApp.exe");
Console.Error.Flush();
return Die(null);
}
static CommandLineArgs ParseCommandLineArgs(string[] args)
{
var rv = new CommandLineArgs();
Action<string> next = null;
try
{
foreach (var arg in args)
{
if (next != null)
{
next(arg);
next = null;
}
else if (arg == "--transport")
next = a => rv.Transport = new Uri(a, UriKind.Absolute);
else if (rv.AppPath == null)
rv.AppPath = arg;
else
PrintUsage();
}
if (rv.AppPath == null || rv.Transport == null)
PrintUsage();
}
catch
{
PrintUsage();
}
return rv;
}
static IAvaloniaRemoteTransportConnection CreateTransport(Uri transport)
{
if (transport.Scheme == "tcp-bson")
{
return new BsonTcpTransport().Connect(IPAddress.Parse(transport.Host), transport.Port).Result;
}
PrintUsage();
return null;
}
interface IAppInitializer
{
Application GetConfiguredApp(IAvaloniaRemoteTransportConnection transport, object obj);
}
class AppInitializer<T> : IAppInitializer where T : AppBuilderBase<T>, new()
{
public Application GetConfiguredApp(IAvaloniaRemoteTransportConnection transport, object obj)
{
var builder = (AppBuilderBase<T>) obj;
builder.UseWindowingSubsystem(() => PreviewerWindowingPlatform.Initialize(transport));
builder.SetupWithoutStarting();
return builder.Instance;
}
}
private const string BuilderMethodName = "BuildAvaloniaApp";
class NeverClose : ICloseable
{
public event EventHandler Closed;
}
static void Main(string[] cmdline)
{
var args = ParseCommandLineArgs(cmdline);
var transport = CreateTransport(args.Transport);
var asm = Assembly.LoadFile(System.IO.Path.GetFullPath(args.AppPath));
var entryPoint = asm.EntryPoint;
if (entryPoint == null)
throw Die($"Assembly {args.AppPath} doesn't have an entry point");
var builderMethod = entryPoint.DeclaringType.GetMethod(BuilderMethodName,
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (builderMethod == null)
throw Die($"{entryPoint.DeclaringType.FullName} doesn't have a method named {BuilderMethodName}");
var appBuilder = builderMethod.Invoke(null, null);
var initializer =(IAppInitializer)Activator.CreateInstance(typeof(AppInitializer<>).MakeGenericType(appBuilder.GetType()));
var app = initializer.GetConfiguredApp(transport, appBuilder);
s_transport = transport;
transport.OnMessage += OnTransportMessage;
transport.OnException += (t, e) => Die(e.ToString());
app.Run(new NeverClose());
}
private static void RebuildPreFlight()
{
PreviewerWindowingPlatform.PreFlightMessages = new List<object>
{
s_supportedPixelFormats,
s_viewportAllocatedMessage
};
}
private static void OnTransportMessage(IAvaloniaRemoteTransportConnection transport, object obj) => Dispatcher.UIThread.InvokeAsync(() =>
{
if (obj is ClientSupportedPixelFormatsMessage formats)
{
s_supportedPixelFormats = formats;
RebuildPreFlight();
}
if (obj is ClientViewportAllocatedMessage viewport)
{
s_viewportAllocatedMessage = viewport;
RebuildPreFlight();
}
if (obj is UpdateXamlMessage xaml)
{
try
{
DesignWindowLoader.LoadDesignerWindow(xaml.Xaml, xaml.AssemblyPath);
s_transport.Send(new UpdateXamlResultMessage());
}
catch (Exception e)
{
s_transport.Send(new UpdateXamlResultMessage
{
Error = e.ToString()
});
}
}
});
}
}

136
src/tools/Avalonia.Designer.HostApp/Stubs.cs

@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Rendering;
namespace Avalonia.Designer.HostApp
{
/// <summary>
/// Popups are no-op
/// </summary>
class WindowStub : IPopupImpl, IWindowImpl
{
public Action Deactivated { get; set; }
public Action Activated { get; set; }
public IPlatformHandle Handle { get; }
public Size MaxClientSize { get; }
public Size ClientSize { get; }
public double Scaling { get; }
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Action Closed { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice();
public Point Position { get; set; }
public Action<Point> PositionChanged { get; set; }
public WindowState WindowState { get; set; }
public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root);
public void Dispose()
{
}
public void Invalidate(Rect rect)
{
}
public void SetInputRoot(IInputRoot inputRoot)
{
}
public Point PointToClient(Point point) => point;
public Point PointToScreen(Point point) => point;
public void SetCursor(IPlatformHandle cursor)
{
}
public void Show()
{
}
public void Hide()
{
}
public void BeginMoveDrag()
{
}
public void BeginResizeDrag(WindowEdge edge)
{
}
public void Activate()
{
}
public void Resize(Size clientSize)
{
}
public void SetTitle(string title)
{
}
public IDisposable ShowDialog() => Disposable.Empty;
public void SetSystemDecorations(bool enabled)
{
}
public void SetIcon(IWindowIconImpl icon)
{
}
}
class ClipboardStub : IClipboard
{
public Task<string> GetTextAsync() => Task.FromResult("");
public Task SetTextAsync(string text) => Task.CompletedTask;
public Task ClearAsync() => Task.CompletedTask;
}
class CursorFactoryStub : IStandardCursorFactory
{
public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "STUB");
}
class IconLoaderStub : IPlatformIconLoader
{
class IconStub : IWindowIconImpl
{
public void Save(Stream outputStream)
{
}
}
public IWindowIconImpl LoadIcon(string fileName) => new IconStub();
public IWindowIconImpl LoadIcon(Stream stream) => new IconStub();
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) => new IconStub();
}
class SystemDialogsStub : ISystemDialogImpl
{
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) =>
Task.FromResult((string[]) null);
public Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) =>
Task.FromResult((string) null);
}
}
Loading…
Cancel
Save