Browse Source

Some hack to make devtools work via websocket

remote-devtools
Nikita Tsukanov 3 years ago
parent
commit
0d68d1d6ac
  1. 1
      samples/ControlCatalog/ControlCatalog.csproj
  2. 1
      samples/ControlCatalog/MainWindow.xaml.cs
  3. 2
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  4. 1
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  5. 2
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts
  6. 1
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  7. 9
      src/Avalonia.Diagnostics/DevToolsExtensions.cs
  8. 24
      src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
  9. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
  10. 17
      src/Avalonia.Diagnostics/Diagnostics/Views/DevToolsRemoteServer.xaml
  11. 91
      src/Avalonia.Diagnostics/Diagnostics/Views/DevToolsRemoteServer.xaml.cs
  12. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

1
samples/ControlCatalog/ControlCatalog.csproj

@ -29,6 +29,7 @@
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
<ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />

1
samples/ControlCatalog/MainWindow.xaml.cs

@ -49,6 +49,7 @@ namespace ControlCatalog
private void InitializeComponent()
{
this.AttachDevTools(new Uri("http://0.0.0.0:15001"));
AvaloniaXamlLoader.Load(this);
}
}

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

@ -315,6 +315,8 @@ namespace Avalonia.Controls.Remote.Server
{
lock (_lock)
{
if (ClientSize.Width < 1 || ClientSize.Height < 1)
return;
if (_lastReceivedFrame != _lastSentFrame || !_invalidated || _supportedFormats == null)
return;

1
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@ -18,6 +18,7 @@
<ProjectReference Include="..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" />

2
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts

@ -52,6 +52,7 @@ export class PreviewerServerConnection {
private onMessage = (msg: MessageEvent) => {
if (typeof msg.data == 'string' || msg.data instanceof String) {
console.log("message: " + msg.data)
const parts = msg.data.split(':');
if (parts[0] == 'frame') {
this.nextFrame = {
@ -64,6 +65,7 @@ export class PreviewerServerConnection {
}
}
} else if (msg.data instanceof ArrayBuffer) {
console.log("received frame");
const arr = new Uint8ClampedArray(msg.data, 0);
const imageData = new ImageData(arr, this.nextFrame.width, this.nextFrame.height);
this.conn.send('frame-received:' + this.nextFrame.sequenceId);

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

@ -12,6 +12,7 @@
<ItemGroup>
<ProjectReference Include="..\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
<ProjectReference Include="..\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
<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.Base\Avalonia.Base.csproj" />

9
src/Avalonia.Diagnostics/DevToolsExtensions.cs

@ -1,5 +1,7 @@
using Avalonia.Controls;
using System;
using Avalonia.Controls;
using Avalonia.Diagnostics;
using Avalonia.Diagnostics.Views;
using Avalonia.Input;
namespace Avalonia
@ -81,5 +83,10 @@ namespace Avalonia
{
DevTools.Attach(application, options);
}
public static IDisposable AttachDevTools(this TopLevel root, Uri listenUri)
{
return DevToolsRemoteServer.Start(root, listenUri);
}
}
}

24
src/Avalonia.Diagnostics/Diagnostics/DevTools.cs

@ -47,10 +47,10 @@ namespace Avalonia.Diagnostics
}
public static IDisposable Open(TopLevel root) =>
Open(Application.Current,new DevToolsOptions(),root as Window);
Open(null, new DevToolsOptions(), root);
public static IDisposable Open(TopLevel root, DevToolsOptions options) =>
Open(Application.Current, options, root as Window);
Open(null, options, root as Window);
private static void DevToolsClosed(object? sender, EventArgs e)
{
@ -103,14 +103,14 @@ namespace Avalonia.Diagnostics
return result;
}
private static IDisposable Open(Application? application, DevToolsOptions options, Window? owner = default)
private static IDisposable Open(Application? application, DevToolsOptions options, TopLevel? owner = default)
{
var focussedControl = KeyboardDevice.Instance?.FocusedElement as IControl;
if (application is null)
{
throw new ArgumentNullException(nameof(application));
}
if (s_open.TryGetValue(application, out var window))
var target = (AvaloniaObject?)application ?? owner;
if (target == null)
throw new ArgumentNullException();
if (s_open.TryGetValue(target, out var window))
{
window.Activate();
window.SelectedControl(focussedControl);
@ -119,17 +119,17 @@ namespace Avalonia.Diagnostics
{
window = new MainWindow
{
Root = new Controls.Application(application),
Root = application != null ? new Controls.Application(application) : target,
Width = options.Size.Width,
Height = options.Size.Height,
};
window.SetOptions(options);
window.SelectedControl(focussedControl);
window.Closed += DevToolsClosed;
s_open.Add(application, window);
if (options.ShowAsChildWindow && owner is { })
s_open.Add(target, window);
if (options.ShowAsChildWindow && owner is Window ownerWindow)
{
window.Show(owner);
window.Show(ownerWindow);
}
else
{

2
src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs

@ -30,7 +30,7 @@ namespace Avalonia.Diagnostics.ViewModels
private IInputRoot? _pointerOverRoot;
private IScreenshotHandler? _screenshotHandler;
private bool _showPropertyType;
private bool _showImplementedInterfaces;
private bool _showImplementedInterfaces = true;
public MainViewModel(AvaloniaObject root)
{

17
src/Avalonia.Diagnostics/Diagnostics/Views/DevToolsRemoteServer.xaml

@ -0,0 +1,17 @@
<EmbeddableControlRoot xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:Avalonia.Diagnostics.Views"
xmlns:diag="clr-namespace:Avalonia.Diagnostics"
x:Class="Avalonia.Diagnostics.Views.DevToolsRemoteServer">
<EmbeddableControlRoot.DataTemplates>
<diag:ViewLocator/>
</EmbeddableControlRoot.DataTemplates>
<EmbeddableControlRoot.Styles>
<SimpleTheme Mode="Light"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml"/>
<StyleInclude Source="avares://Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.axaml" />
<StyleInclude Source="avares://Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml" />
</EmbeddableControlRoot.Styles>
<views:MainView/>
</EmbeddableControlRoot>

91
src/Avalonia.Diagnostics/Diagnostics/Views/DevToolsRemoteServer.xaml.cs

@ -0,0 +1,91 @@
using System;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Embedding;
using Avalonia.Controls.Remote;
using Avalonia.Controls.Remote.Server;
using Avalonia.DesignerSupport.Remote.HtmlTransport;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Markup.Xaml;
using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Viewport;
using Avalonia.Styling;
namespace Avalonia.Diagnostics.Views;
internal class DevToolsRemoteServer : EmbeddableControlRoot, IStyleHost, IStyledElement
{
IStyleHost? IStyleHost.StylingParent => null;
[Obsolete("Compiler-only", true)]
public DevToolsRemoteServer()
{
}
public DevToolsRemoteServer(IAvaloniaRemoteTransportConnection transport) : base(new RemoteServerTopLevelImpl(transport))
{
AvaloniaXamlLoader.Load(this);
Width = Height = 1024;
if (Theme is null && this.FindResource(typeof(EmbeddableControlRoot)) is ControlTheme topLevelTheme)
Theme = topLevelTheme;
}
public Type StyleKey => typeof(EmbeddableControlRoot);
class ZeroSignal : IAvaloniaRemoteTransportConnection
{
public void Dispose()
{
}
public Task Send(object data)
{
return Task.CompletedTask;
}
public void SendMessage(object message) => OnMessage?.Invoke(this, message);
public event Action<IAvaloniaRemoteTransportConnection, object>? OnMessage;
public event Action<IAvaloniaRemoteTransportConnection, Exception>? OnException;
public void Start()
{
}
}
public static IDisposable Start(TopLevel target, Uri listenUri)
{
var signal = new ZeroSignal();
var transport = new HtmlWebSocketTransport(signal, listenUri);
var vm = new MainViewModel(target);
var server = new DevToolsRemoteServer(transport)
{
DataContext = vm,
Content = vm
};
server.ClientSize = new Size(1024, 1024);
server.Prepare();
server.Renderer.Start();
transport.Start();
signal.SendMessage(new ClientViewportAllocatedMessage()
{
Height = 1024,
Width = 1024,
DpiX = 96,
DpiY = 96
});
DevTools.Open(server);
return Disposable.Create(() =>
{
server.Renderer.Stop();
server.Dispose();
transport.Dispose();
});
}
}

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

@ -57,7 +57,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
// hack can be removed.
if (previousWasControlTheme &&
parent is IResourceProvider hack &&
hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.MainWindow" &&
(hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.MainWindow"
|| hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.DevToolsRemoteServer"
) &&
hack.Owner.TryGetResource(ResourceKey, out value))
{
return ColorToBrushConverter.Convert(value, targetType);

Loading…
Cancel
Save