Browse Source

fix(DevTools): Debug Application without ApplicationLifetime

pull/10510/head
Giuseppe Lippolis 3 years ago
parent
commit
7f9cbb6aed
  1. 1
      Avalonia.Desktop.slnf
  2. 17
      Avalonia.sln
  3. 7
      samples/AppWithoutLifetime/App.axaml
  4. 12
      samples/AppWithoutLifetime/App.axaml.cs
  5. 21
      samples/AppWithoutLifetime/AppWithoutLifetime.csproj
  6. 13
      samples/AppWithoutLifetime/MainWindow.axaml
  7. 30
      samples/AppWithoutLifetime/MainWindow.axaml.cs
  8. 28
      samples/AppWithoutLifetime/Program.cs
  9. 9
      samples/AppWithoutLifetime/Sub.axaml
  10. 24
      samples/AppWithoutLifetime/Sub.axaml.cs
  11. 18
      samples/AppWithoutLifetime/app.manifest
  12. 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  13. 1
      src/Avalonia.Controls/DesktopApplicationExtensions.cs
  14. 78
      src/Avalonia.Diagnostics/Diagnostics/DevTools.cs

1
Avalonia.Desktop.slnf

@ -3,6 +3,7 @@
"path": "Avalonia.sln",
"projects": [
"packages\\Avalonia\\Avalonia.csproj",
"samples\\AppWithoutLifetime\\AppWithoutLifetime.csproj",
"samples\\ControlCatalog.NetCore\\ControlCatalog.NetCore.csproj",
"samples\\ControlCatalog\\ControlCatalog.csproj",
"samples\\GpuInterop\\GpuInterop.csproj",

17
Avalonia.sln

@ -244,13 +244,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepeater.UnitTests", "tests\Avalonia.Controls.ItemsRepeater.UnitTests\Avalonia.Controls.ItemsRepeater.UnitTests.csproj", "{F4E36AA8-814E-4704-BC07-291F70F45193}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppWithoutLifetime", "samples\AppWithoutLifetime\AppWithoutLifetime.csproj", "{F8928267-688E-4A51-989C-612A72446D33}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -595,6 +597,10 @@ Global
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.Build.0 = Release|Any CPU
{F8928267-688E-4A51-989C-612A72446D33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8928267-688E-4A51-989C-612A72446D33}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8928267-688E-4A51-989C-612A72446D33}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8928267-688E-4A51-989C-612A72446D33}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -661,10 +667,11 @@ Global
{75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{F8928267-688E-4A51-989C-612A72446D33} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

7
samples/AppWithoutLifetime/App.axaml

@ -0,0 +1,7 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AppWithoutLifetime.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

12
samples/AppWithoutLifetime/App.axaml.cs

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

21
samples/AppWithoutLifetime/AppWithoutLifetime.csproj

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.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.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
</ItemGroup>
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" />
<Import Project="..\..\build\BuildTargets.targets" />
</Project>

13
samples/AppWithoutLifetime/MainWindow.axaml

@ -0,0 +1,13 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AppWithoutLifetime.MainWindow"
Title="AppWithoutLifetime">
<StackPanel>
<TextBlock Text="Welcome to Avalonia!"/>
<CheckBox Content="Welcome to Avalonia!"/>
<Button Click="Open" Content="Open"/>
</StackPanel>
</Window>

30
samples/AppWithoutLifetime/MainWindow.axaml.cs

@ -0,0 +1,30 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace AppWithoutLifetime;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
protected override void OnLoaded()
{
this.AttachDevTools();
base.OnLoaded();
}
public void Open(object sender, RoutedEventArgs e)
{
new Sub().Show(this);
}
}

28
samples/AppWithoutLifetime/Program.cs

@ -0,0 +1,28 @@
using Avalonia;
using Avalonia.Controls;
using System;
namespace AppWithoutLifetime;
class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args)
{
BuildAvaloniaApp().Start(AppMain, args);
}
private static void AppMain(Application app, string[] args)
{
app.Run(new MainWindow());
}
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToTrace();
}

9
samples/AppWithoutLifetime/Sub.axaml

@ -0,0 +1,9 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AppWithoutLifetime.Sub"
Title="Window1">
Welcome to Avalonia Sub!
</Window>

24
samples/AppWithoutLifetime/Sub.axaml.cs

@ -0,0 +1,24 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace AppWithoutLifetime;
public partial class Sub : Window
{
public Sub()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
protected override void OnLoaded()
{
this.AttachDevTools();
base.OnLoaded();
}
}

18
samples/AppWithoutLifetime/app.manifest

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embeded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="AppWithoutLifetime"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

1
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -15,6 +15,7 @@
<InternalsVisibleTo Include="Avalonia.Controls.ItemsRepeater, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)"/>
<InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup>
</Project>

1
src/Avalonia.Controls/DesktopApplicationExtensions.cs

@ -1,6 +1,5 @@
using System;
using System.Threading;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;

78
src/Avalonia.Diagnostics/Diagnostics/DevTools.cs

@ -9,7 +9,7 @@ using Avalonia.Reactive;
namespace Avalonia.Diagnostics
{
public static class DevTools
internal static class DevTools
{
private static readonly Dictionary<AvaloniaObject, MainWindow> s_open =
new Dictionary<AvaloniaObject, MainWindow>();
@ -28,7 +28,7 @@ namespace Avalonia.Diagnostics
{
if (s_attachedToApplication == true)
{
throw new ArgumentException("DevTools already attached to application", nameof(root));
throw new ArgumentException("DevTools already attached to application.", nameof(root));
}
void PreviewKeyDown(object? sender, KeyEventArgs e)
@ -45,50 +45,31 @@ namespace Avalonia.Diagnostics
RoutingStrategies.Tunnel);
}
public static IDisposable Open(TopLevel root) =>
Open(Application.Current,new DevToolsOptions(),root as Window);
private static IDisposable Open(TopLevel root, DevToolsOptions options) =>
Open(default, options, root);
public static IDisposable Open(TopLevel root, DevToolsOptions options) =>
Open(Application.Current, options, root as Window);
private static void DevToolsClosed(object? sender, EventArgs e)
{
var window = (MainWindow)sender!;
window.Closed -= DevToolsClosed;
if (window.Root is Controls.Application host)
{
s_open.Remove(host.Instance);
}
else
{
s_open.Remove(window.Root!);
}
}
internal static IDisposable Attach(Application? application, DevToolsOptions options, Window? owner = null)
internal static IDisposable Attach(Application application, DevToolsOptions options)
{
if (application is null)
{
throw new ArgumentNullException(nameof(application));
}
var openedDisposable = new SerialDisposableValue();
var result = new CompositeDisposable(2);
result.Add(openedDisposable);
// Skip if call on Design Mode
if (!Avalonia.Controls.Design.IsDesignMode
if (!Design.IsDesignMode
&& !s_attachedToApplication)
{
var lifeTime = application.ApplicationLifetime
as Avalonia.Controls.ApplicationLifetimes.IControlledApplicationLifetime;
as Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime;
if (lifeTime is null)
{
throw new ArgumentNullException(nameof(Application.ApplicationLifetime));
throw new ArgumentNullException(nameof(application), "DevTools can only attach to applications that support IClassicDesktopStyleApplicationLifetime.");
}
var owner = TopLevel.GetTopLevel(lifeTime.MainWindow)
?? throw new ArgumentException(nameof(application), "It can't retrieve TopLevel.");
if (application.InputManager is { })
{
s_attachedToApplication = true;
@ -102,20 +83,23 @@ namespace Avalonia.Diagnostics
openedDisposable.Disposable = Open(application, options, owner);
}
}));
}
}
return result;
}
private static IDisposable Open(Application? application, DevToolsOptions options, Window? owner = default)
private static IDisposable Open(Application? application, DevToolsOptions options, TopLevel owner)
{
var focussedControl = KeyboardDevice.Instance?.FocusedElement as Control;
if (application is null)
AvaloniaObject root = owner;
AvaloniaObject key = owner;
if (application is not null)
{
throw new ArgumentNullException(nameof(application));
root = new Controls.Application(application);
key = application;
}
if (s_open.TryGetValue(application, out var window))
if (s_open.TryGetValue(key, out var window))
{
window.Activate();
window.SelectedControl(focussedControl);
@ -124,17 +108,17 @@ namespace Avalonia.Diagnostics
{
window = new MainWindow
{
Root = new Controls.Application(application),
Root = root,
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(key, window);
if (options.ShowAsChildWindow && owner is Window ow)
{
window.Show(owner);
window.Show(ow);
}
else
{
@ -143,5 +127,19 @@ namespace Avalonia.Diagnostics
}
return Disposable.Create(() => window?.Close());
}
private static void DevToolsClosed(object? sender, EventArgs e)
{
var window = (MainWindow)sender!;
window.Closed -= DevToolsClosed;
if (window.Root is Controls.Application host)
{
s_open.Remove(host.Instance);
}
else
{
s_open.Remove(window.Root!);
}
}
}
}

Loading…
Cancel
Save