|
|
@ -1,5 +1,7 @@ |
|
|
using System; |
|
|
using System; |
|
|
using System.Collections.Generic; |
|
|
using System.Collections.Generic; |
|
|
|
|
|
using System.Collections.Specialized; |
|
|
|
|
|
using System.Linq; |
|
|
using Avalonia.Controls; |
|
|
using Avalonia.Controls; |
|
|
using Avalonia.Diagnostics.Views; |
|
|
using Avalonia.Diagnostics.Views; |
|
|
using Avalonia.Input; |
|
|
using Avalonia.Input; |
|
|
@ -9,12 +11,9 @@ using Avalonia.Reactive; |
|
|
|
|
|
|
|
|
namespace Avalonia.Diagnostics |
|
|
namespace Avalonia.Diagnostics |
|
|
{ |
|
|
{ |
|
|
public static class DevTools |
|
|
internal static class DevTools |
|
|
{ |
|
|
{ |
|
|
private static readonly Dictionary<AvaloniaObject, MainWindow> s_open = |
|
|
private static readonly Dictionary<IDevToolsTopLevelGroup, MainWindow> s_open = new(); |
|
|
new Dictionary<AvaloniaObject, MainWindow>(); |
|
|
|
|
|
|
|
|
|
|
|
private static bool s_attachedToApplication; |
|
|
|
|
|
|
|
|
|
|
|
public static IDisposable Attach(TopLevel root, KeyGesture gesture) |
|
|
public static IDisposable Attach(TopLevel root, KeyGesture gesture) |
|
|
{ |
|
|
{ |
|
|
@ -26,11 +25,6 @@ namespace Avalonia.Diagnostics |
|
|
|
|
|
|
|
|
public static IDisposable Attach(TopLevel root, DevToolsOptions options) |
|
|
public static IDisposable Attach(TopLevel root, DevToolsOptions options) |
|
|
{ |
|
|
{ |
|
|
if (s_attachedToApplication == true) |
|
|
|
|
|
{ |
|
|
|
|
|
throw new ArgumentException("DevTools already attached to application", nameof(root)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void PreviewKeyDown(object? sender, KeyEventArgs e) |
|
|
void PreviewKeyDown(object? sender, KeyEventArgs e) |
|
|
{ |
|
|
{ |
|
|
if (options.Gesture.Matches(e)) |
|
|
if (options.Gesture.Matches(e)) |
|
|
@ -39,109 +33,116 @@ namespace Avalonia.Diagnostics |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return root.AddDisposableHandler( |
|
|
return (root ?? throw new ArgumentNullException(nameof(root))).AddDisposableHandler( |
|
|
InputElement.KeyDownEvent, |
|
|
InputElement.KeyDownEvent, |
|
|
PreviewKeyDown, |
|
|
PreviewKeyDown, |
|
|
RoutingStrategies.Tunnel); |
|
|
RoutingStrategies.Tunnel); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public static IDisposable Open(TopLevel root) => |
|
|
public static IDisposable Open(TopLevel root, DevToolsOptions options) => |
|
|
Open(Application.Current,new DevToolsOptions(),root as Window); |
|
|
Open(new SingleViewTopLevelGroup(root), options, root as Window, null); |
|
|
|
|
|
|
|
|
public static IDisposable Open(TopLevel root, DevToolsOptions options) => |
|
|
|
|
|
Open(Application.Current, options, root as Window); |
|
|
|
|
|
|
|
|
|
|
|
private static void DevToolsClosed(object? sender, EventArgs e) |
|
|
internal static IDisposable Open(IDevToolsTopLevelGroup group, DevToolsOptions options) => |
|
|
{ |
|
|
Open(group, options, null, null); |
|
|
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 openedDisposable = new SerialDisposableValue(); |
|
|
var result = new CompositeDisposable(2); |
|
|
var result = new CompositeDisposable(2); |
|
|
result.Add(openedDisposable); |
|
|
result.Add(openedDisposable); |
|
|
|
|
|
|
|
|
// Skip if call on Design Mode
|
|
|
// Skip if call on Design Mode
|
|
|
if (!Avalonia.Controls.Design.IsDesignMode |
|
|
if (!Design.IsDesignMode) |
|
|
&& !s_attachedToApplication) |
|
|
|
|
|
{ |
|
|
{ |
|
|
|
|
|
|
|
|
var lifeTime = application.ApplicationLifetime |
|
|
var lifeTime = application.ApplicationLifetime |
|
|
as Avalonia.Controls.ApplicationLifetimes.IControlledApplicationLifetime; |
|
|
as Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime; |
|
|
|
|
|
|
|
|
if (lifeTime is null) |
|
|
if (lifeTime is null) |
|
|
{ |
|
|
{ |
|
|
throw new ArgumentNullException(nameof(Application.ApplicationLifetime)); |
|
|
throw new ArgumentNullException(nameof(application), "DevTools can only attach to applications that support IClassicDesktopStyleApplicationLifetime."); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (application.InputManager is { }) |
|
|
if (application.InputManager is not null) |
|
|
{ |
|
|
{ |
|
|
s_attachedToApplication = true; |
|
|
|
|
|
|
|
|
|
|
|
result.Add(application.InputManager.PreProcess.Subscribe(e => |
|
|
result.Add(application.InputManager.PreProcess.Subscribe(e => |
|
|
{ |
|
|
{ |
|
|
|
|
|
var owner = lifeTime.MainWindow; |
|
|
|
|
|
|
|
|
if (e is RawKeyEventArgs keyEventArgs |
|
|
if (e is RawKeyEventArgs keyEventArgs |
|
|
&& keyEventArgs.Type == RawKeyEventType.KeyUp |
|
|
&& keyEventArgs.Type == RawKeyEventType.KeyUp |
|
|
&& options.Gesture.Matches(keyEventArgs)) |
|
|
&& options.Gesture.Matches(keyEventArgs)) |
|
|
{ |
|
|
{ |
|
|
openedDisposable.Disposable = Open(application, options, owner); |
|
|
openedDisposable.Disposable = |
|
|
|
|
|
Open(new ClassicDesktopStyleApplicationLifetimeTopLevelGroup(lifeTime), options, |
|
|
|
|
|
owner, application); |
|
|
|
|
|
e.Handled = true; |
|
|
} |
|
|
} |
|
|
})); |
|
|
})); |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return result; |
|
|
return result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private static IDisposable Open(Application? application, DevToolsOptions options, Window? owner = default) |
|
|
private static IDisposable Open(IDevToolsTopLevelGroup topLevelGroup, DevToolsOptions options, |
|
|
|
|
|
Window? owner, Application? app) |
|
|
{ |
|
|
{ |
|
|
var focussedControl = KeyboardDevice.Instance?.FocusedElement as Control; |
|
|
var focussedControl = KeyboardDevice.Instance?.FocusedElement as Control; |
|
|
if (application is null) |
|
|
AvaloniaObject root = topLevelGroup switch |
|
|
{ |
|
|
{ |
|
|
throw new ArgumentNullException(nameof(application)); |
|
|
ClassicDesktopStyleApplicationLifetimeTopLevelGroup gr => new Controls.Application(gr, app ?? Application.Current!), |
|
|
} |
|
|
SingleViewTopLevelGroup gr => gr.Items.First(), |
|
|
if (s_open.TryGetValue(application, out var window)) |
|
|
_ => new Controls.TopLevelGroup(topLevelGroup) |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// If single static toplevel is already visible in another devtools window, focus it.
|
|
|
|
|
|
if (s_open.TryGetValue(topLevelGroup, out var mainWindow)) |
|
|
{ |
|
|
{ |
|
|
window.Activate(); |
|
|
mainWindow.Activate(); |
|
|
window.SelectedControl(focussedControl); |
|
|
mainWindow.SelectedControl(focussedControl); |
|
|
|
|
|
return Disposable.Empty; |
|
|
} |
|
|
} |
|
|
else |
|
|
if (topLevelGroup.Items.Count == 1 && topLevelGroup.Items is not INotifyCollectionChanged) |
|
|
{ |
|
|
{ |
|
|
window = new MainWindow |
|
|
var singleTopLevel = topLevelGroup.Items.First(); |
|
|
{ |
|
|
|
|
|
Root = new Controls.Application(application), |
|
|
foreach (var group in s_open) |
|
|
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 { }) |
|
|
|
|
|
{ |
|
|
{ |
|
|
window.Show(owner); |
|
|
if (group.Key.Items.Contains(singleTopLevel)) |
|
|
} |
|
|
{ |
|
|
else |
|
|
group.Value.Activate(); |
|
|
{ |
|
|
group.Value.SelectedControl(focussedControl); |
|
|
window.Show(); |
|
|
return Disposable.Empty; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return Disposable.Create(() => window?.Close()); |
|
|
|
|
|
|
|
|
var window = new MainWindow |
|
|
|
|
|
{ |
|
|
|
|
|
Root = root, |
|
|
|
|
|
Width = options.Size.Width, |
|
|
|
|
|
Height = options.Size.Height, |
|
|
|
|
|
Tag = topLevelGroup |
|
|
|
|
|
}; |
|
|
|
|
|
window.SetOptions(options); |
|
|
|
|
|
window.SelectedControl(focussedControl); |
|
|
|
|
|
window.Closed += DevToolsClosed; |
|
|
|
|
|
s_open.Add(topLevelGroup, window); |
|
|
|
|
|
if (options.ShowAsChildWindow && owner is not null) |
|
|
|
|
|
{ |
|
|
|
|
|
window.Show(owner); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
window.Show(); |
|
|
|
|
|
} |
|
|
|
|
|
return Disposable.Create(() => window.Close()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static void DevToolsClosed(object? sender, EventArgs e) |
|
|
|
|
|
{ |
|
|
|
|
|
var window = (MainWindow)sender!; |
|
|
|
|
|
window.Closed -= DevToolsClosed; |
|
|
|
|
|
s_open.Remove((IDevToolsTopLevelGroup)window.Tag!); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|