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"> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
<TargetFramework>netstandard2.0</TargetFramework> |
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
</PropertyGroup> |
</PropertyGroup> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" /> |
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" /> |
||||
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" /> |
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" /> |
||||
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" /> |
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" /> |
||||
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> |
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" /> |
||||
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" /> |
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" /> |
||||
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> |
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</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