34 changed files with 977 additions and 139 deletions
@ -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> |
|||
@ -0,0 +1,14 @@ |
|||
using Avalonia; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace Previewer |
|||
{ |
|||
public class App : Application |
|||
{ |
|||
public override void Initialize() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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> |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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> |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using Avalonia; |
|||
|
|||
namespace Previewer |
|||
{ |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
AppBuilder.Configure<App>().UsePlatformDetect().Start<MainWindow>(); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -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> |
|||
@ -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> |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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) |
|||
{ |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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() |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue