Browse Source

Enable nullable on Avalonia.Diagnostics

pull/5998/head
Max Katz 5 years ago
parent
commit
522f6b97ae
  1. 4
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  2. 8
      src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs
  3. 11
      src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToOpacityConverter.cs
  4. 4
      src/Avalonia.Diagnostics/Diagnostics/Converters/EnumToCheckedConverter.cs
  5. 4
      src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
  6. 4
      src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs
  7. 4
      src/Avalonia.Diagnostics/Diagnostics/Models/EventChainLink.cs
  8. 42
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
  9. 7
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
  10. 3
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs
  11. 16
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
  12. 10
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs
  13. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventOwnerTreeNode.cs
  14. 19
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs
  15. 6
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNodeBase.cs
  16. 11
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventsPageViewModel.cs
  17. 17
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs
  18. 15
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs
  19. 8
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs
  20. 19
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
  21. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs
  22. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ResourceSetterViewModel.cs
  23. 4
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs
  24. 16
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs
  25. 29
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs
  26. 14
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
  27. 10
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs
  28. 8
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
  29. 8
      src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs
  30. 6
      src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs
  31. 31
      src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
  32. 14
      src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
  33. 2
      src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs

4
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -3,12 +3,16 @@
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace>
<PackageId>Avalonia.Diagnostics</PackageId>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />

8
src/Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.cs

@ -10,8 +10,8 @@ namespace Avalonia.Diagnostics.Controls
AvaloniaProperty.RegisterDirect<ThicknessEditor, Thickness>(nameof(Thickness), o => o.Thickness,
(o, v) => o.Thickness = v, defaultBindingMode: BindingMode.TwoWay);
public static readonly DirectProperty<ThicknessEditor, string> HeaderProperty =
AvaloniaProperty.RegisterDirect<ThicknessEditor, string>(nameof(Header), o => o.Header,
public static readonly DirectProperty<ThicknessEditor, string?> HeaderProperty =
AvaloniaProperty.RegisterDirect<ThicknessEditor, string?>(nameof(Header), o => o.Header,
(o, v) => o.Header = v);
public static readonly DirectProperty<ThicknessEditor, bool> IsPresentProperty =
@ -36,7 +36,7 @@ namespace Avalonia.Diagnostics.Controls
AvaloniaProperty.Register<ThicknessEditor, IBrush>(nameof(Highlight));
private Thickness _thickness;
private string _header;
private string? _header;
private bool _isPresent = true;
private double _left;
private double _top;
@ -50,7 +50,7 @@ namespace Avalonia.Diagnostics.Controls
set => SetAndRaise(ThicknessProperty, ref _thickness, value);
}
public string Header
public string? Header
{
get => _header;
set => SetAndRaise(HeaderProperty, ref _header, value);

11
src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToOpacityConverter.cs

@ -8,12 +8,17 @@ namespace Avalonia.Diagnostics.Converters
{
public double Opacity { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return (bool)value ? 1d : Opacity;
if (value is bool boolean && boolean)
{
return 1d;
}
return Opacity;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

4
src/Avalonia.Diagnostics/Diagnostics/Converters/EnumToCheckedConverter.cs

@ -7,12 +7,12 @@ namespace Avalonia.Diagnostics.Converters
{
internal class EnumToCheckedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Equals(value, parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is bool isChecked && isChecked)
{

4
src/Avalonia.Diagnostics/Diagnostics/DevTools.cs

@ -6,8 +6,6 @@ using Avalonia.Diagnostics.Views;
using Avalonia.Input;
using Avalonia.Interactivity;
#nullable enable
namespace Avalonia.Diagnostics
{
public static class DevTools
@ -74,7 +72,7 @@ namespace Avalonia.Diagnostics
private static void DevToolsClosed(object sender, EventArgs e)
{
var window = (MainWindow)sender;
s_open.Remove(window.Root);
s_open.Remove(window.Root!);
window.Closed -= DevToolsClosed;
}
}

4
src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs

@ -22,8 +22,8 @@ The following commands are available:
clear(): Clear the output history
";
public dynamic e { get; internal set; }
public dynamic root { get; internal set; }
public dynamic? e { get; internal set; }
public dynamic? root { get; internal set; }
internal static object NoOutput { get; } = new object();

4
src/Avalonia.Diagnostics/Diagnostics/Models/EventChainLink.cs

@ -7,9 +7,7 @@ namespace Avalonia.Diagnostics.Models
{
public EventChainLink(object handler, bool handled, RoutingStrategies route)
{
Contract.Requires<ArgumentNullException>(handler != null);
Handler = handler;
Handler = handler ?? throw new ArgumentNullException(nameof(handler));
Handled = handled;
Route = route;
}

42
src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs

@ -1,5 +1,4 @@
using System.ComponentModel;
using Avalonia.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Avalonia.Diagnostics.ViewModels
{
@ -7,7 +6,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
private readonly AvaloniaObject _target;
private string _type;
private object _value;
private object? _value;
private string _priority;
private string _group;
@ -20,12 +19,6 @@ namespace Avalonia.Diagnostics.ViewModels
$"[{property.OwnerType.Name}.{property.Name}]" :
property.Name;
if (property.IsDirect)
{
_group = "Properties";
Priority = "Direct";
}
Update();
}
@ -34,11 +27,7 @@ namespace Avalonia.Diagnostics.ViewModels
public override string Name { get; }
public bool IsAttached => Property.IsAttached;
public string Priority
{
get => _priority;
private set => RaiseAndSetIfChanged(ref _priority, value);
}
public string Priority => _priority;
public override string Type => _type;
@ -56,40 +45,37 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
public override string Group
{
get => _group;
}
public override string Group => _group;
[MemberNotNull(nameof(_type), nameof(_group), nameof(_priority))]
public override void Update()
{
if (Property.IsDirect)
{
RaiseAndSetIfChanged(ref _value, _target.GetValue(Property), nameof(Value));
RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
RaiseAndSetIfChanged(ref _type, _value?.GetType().Name ?? Property.PropertyType.Name, nameof(Type));
RaiseAndSetIfChanged(ref _priority, "Direct", nameof(Priority));
_group = "Properties";
}
else
{
var val = _target.GetDiagnostic(Property);
RaiseAndSetIfChanged(ref _value, val?.Value, nameof(Value));
RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
RaiseAndSetIfChanged(ref _type, _value?.GetType().Name ?? Property.PropertyType.Name, nameof(Type));
if (val != null)
{
SetGroup(IsAttached ? "Attached Properties" : "Properties");
Priority = val.Priority.ToString();
RaiseAndSetIfChanged(ref _priority, val.Priority.ToString(), nameof(Priority));
RaiseAndSetIfChanged(ref _group, IsAttached ? "Attached Properties" : "Properties", nameof(Group));
}
else
{
SetGroup(Priority = "Unset");
RaiseAndSetIfChanged(ref _priority, "Unset", nameof(Priority));
RaiseAndSetIfChanged(ref _group, "Unset", nameof(Group));
}
}
}
private void SetGroup(string group)
{
RaiseAndSetIfChanged(ref _group, group, nameof(Group));
}
}
}

7
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs

@ -1,4 +1,4 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace Avalonia.Diagnostics.ViewModels
@ -7,7 +7,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
private readonly object _target;
private string _type;
private object _value;
private object? _value;
public ClrPropertyViewModel(object o, PropertyInfo property)
{
@ -47,11 +47,12 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
[MemberNotNull(nameof(_type))]
public override void Update()
{
var val = Property.GetValue(_target);
RaiseAndSetIfChanged(ref _value, val, nameof(Value));
RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
RaiseAndSetIfChanged(ref _type, _value?.GetType().Name ?? Property.PropertyType.Name, nameof(Type));
}
}
}

3
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs

@ -15,11 +15,12 @@ namespace Avalonia.Diagnostics.ViewModels
private int _historyIndex = -1;
private string _input;
private bool _isVisible;
private ScriptState<object> _state;
private ScriptState<object>? _state;
public ConsoleViewModel(Action<ConsoleContext> updateContext)
{
_context = new ConsoleContext(this);
_input = string.Empty;
_updateContext = updateContext;
}

16
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs

@ -18,10 +18,10 @@ namespace Avalonia.Diagnostics.ViewModels
{
private readonly IVisual _control;
private readonly IDictionary<object, List<PropertyViewModel>> _propertyIndex;
private AvaloniaPropertyViewModel _selectedProperty;
private AvaloniaPropertyViewModel? _selectedProperty;
private bool _snapshotStyles;
private bool _showInactiveStyles;
private string _styleStatus;
private string? _styleStatus;
public ControlDetailsViewModel(TreePageViewModel treePage, IVisual control)
{
@ -83,7 +83,8 @@ namespace Avalonia.Diagnostics.ViewModels
{
foreach (var setter in style.Setters)
{
if (setter is Setter regularSetter)
if (setter is Setter regularSetter
&& regularSetter.Property != null)
{
var setterValue = regularSetter.Value;
@ -115,13 +116,14 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
private (object resourceKey, bool isDynamic)? GetResourceInfo(object value)
private (object resourceKey, bool isDynamic)? GetResourceInfo(object? value)
{
if (value is StaticResourceExtension staticResource)
{
return (staticResource.ResourceKey, false);
}
else if (value is DynamicResourceExtension dynamicResource)
else if (value is DynamicResourceExtension dynamicResource
&& dynamicResource.ResourceKey != null)
{
return (dynamicResource.ResourceKey, true);
}
@ -137,7 +139,7 @@ namespace Avalonia.Diagnostics.ViewModels
public ObservableCollection<PseudoClassViewModel> PseudoClasses { get; }
public AvaloniaPropertyViewModel SelectedProperty
public AvaloniaPropertyViewModel? SelectedProperty
{
get => _selectedProperty;
set => RaiseAndSetIfChanged(ref _selectedProperty, value);
@ -155,7 +157,7 @@ namespace Avalonia.Diagnostics.ViewModels
set => RaiseAndSetIfChanged(ref _showInactiveStyles, value);
}
public string StyleStatus
public string? StyleStatus
{
get => _styleStatus;
set => RaiseAndSetIfChanged(ref _styleStatus, value);

10
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlLayoutViewModel.cs

@ -12,14 +12,14 @@ namespace Avalonia.Diagnostics.ViewModels
private readonly IVisual _control;
private Thickness _borderThickness;
private double _height;
private string _heightConstraint;
private string? _heightConstraint;
private HorizontalAlignment _horizontalAlignment;
private Thickness _marginThickness;
private Thickness _paddingThickness;
private bool _updatingFromControl;
private VerticalAlignment _verticalAlignment;
private double _width;
private string _widthConstraint;
private string? _widthConstraint;
public ControlLayoutViewModel(IVisual control)
{
@ -80,13 +80,13 @@ namespace Avalonia.Diagnostics.ViewModels
private set => RaiseAndSetIfChanged(ref _height, value);
}
public string WidthConstraint
public string? WidthConstraint
{
get => _widthConstraint;
private set => RaiseAndSetIfChanged(ref _widthConstraint, value);
}
public string HeightConstraint
public string? HeightConstraint
{
get => _heightConstraint;
private set => RaiseAndSetIfChanged(ref _heightConstraint, value);
@ -112,7 +112,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
if (_control is IAvaloniaObject ao)
{
string CreateConstraintInfo(StyledProperty<double> minProperty, StyledProperty<double> maxProperty)
string? CreateConstraintInfo(StyledProperty<double> minProperty, StyledProperty<double> maxProperty)
{
bool hasMin = ao.IsSet(minProperty);
bool hasMax = ao.IsSet(maxProperty);

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

@ -27,7 +27,7 @@ namespace Avalonia.Diagnostics.ViewModels
if (_updateChildren && value != null)
{
foreach (var child in Children)
foreach (var child in Children!)
{
try
{

19
src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs

@ -11,16 +11,13 @@ namespace Avalonia.Diagnostics.ViewModels
{
private readonly EventsPageViewModel _parentViewModel;
private bool _isRegistered;
private FiredEvent _currentEvent;
private FiredEvent? _currentEvent;
public EventTreeNode(EventOwnerTreeNode parent, RoutedEvent @event, EventsPageViewModel vm)
: base(parent, @event.Name)
{
Contract.Requires<ArgumentNullException>(@event != null);
Contract.Requires<ArgumentNullException>(vm != null);
Event = @event;
_parentViewModel = vm;
Event = @event ?? throw new ArgumentNullException(nameof(@event));
_parentViewModel = vm ?? throw new ArgumentNullException(nameof(vm));
}
public RoutedEvent Event { get; }
@ -73,7 +70,7 @@ namespace Avalonia.Diagnostics.ViewModels
var handled = e.Handled;
var route = e.Route;
Action handler = delegate
void handler()
{
if (_currentEvent == null || !_currentEvent.IsPartOfSameEventChain(e))
{
@ -98,14 +95,16 @@ namespace Avalonia.Diagnostics.ViewModels
private static bool BelongsToDevTool(IVisual v)
{
while (v != null)
var current = v;
while (current != null)
{
if (v is MainView || v is MainWindow)
if (current is MainView || current is MainWindow)
{
return true;
}
v = v.VisualParent;
current = current.VisualParent;
}
return false;

6
src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNodeBase.cs

@ -10,14 +10,14 @@ namespace Avalonia.Diagnostics.ViewModels
private bool? _isEnabled = false;
private bool _isVisible;
protected EventTreeNodeBase(EventTreeNodeBase parent, string text)
protected EventTreeNodeBase(EventTreeNodeBase? parent, string text)
{
Parent = parent;
Text = text;
IsVisible = true;
}
public IAvaloniaReadOnlyList<EventTreeNodeBase> Children
public IAvaloniaReadOnlyList<EventTreeNodeBase>? Children
{
get;
protected set;
@ -41,7 +41,7 @@ namespace Avalonia.Diagnostics.ViewModels
set => RaiseAndSetIfChanged(ref _isVisible, value);
}
public EventTreeNodeBase Parent
public EventTreeNodeBase? Parent
{
get;
}

11
src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventsPageViewModel.cs

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Diagnostics.Models;
@ -23,8 +22,8 @@ namespace Avalonia.Diagnostics.ViewModels
};
private readonly MainViewModel _mainViewModel;
private FiredEvent _selectedEvent;
private EventTreeNodeBase _selectedNode;
private FiredEvent? _selectedEvent;
private EventTreeNodeBase? _selectedNode;
public EventsPageViewModel(MainViewModel mainViewModel)
{
@ -48,13 +47,13 @@ namespace Avalonia.Diagnostics.ViewModels
public ObservableCollection<FiredEvent> RecordedEvents { get; } = new ObservableCollection<FiredEvent>();
public FiredEvent SelectedEvent
public FiredEvent? SelectedEvent
{
get => _selectedEvent;
set => RaiseAndSetIfChanged(ref _selectedEvent, value);
}
public EventTreeNodeBase SelectedNode
public EventTreeNodeBase? SelectedNode
{
get => _selectedNode;
set => RaiseAndSetIfChanged(ref _selectedNode, value);
@ -99,7 +98,7 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
static EventTreeNodeBase FindNode(EventTreeNodeBase node, RoutedEvent eventType)
static EventTreeNodeBase? FindNode(EventTreeNodeBase node, RoutedEvent eventType)
{
if (node is EventTreeNode eventNode && eventNode.Event == eventType)
{

17
src/Avalonia.Diagnostics/Diagnostics/ViewModels/FilterViewModel.cs

@ -11,10 +11,13 @@ namespace Avalonia.Diagnostics.ViewModels
private readonly Dictionary<string, string> _errors = new Dictionary<string, string>();
private string _filterString = string.Empty;
private bool _useRegexFilter, _useCaseSensitiveFilter, _useWholeWordFilter;
private string _processedFilter;
private Regex _filterRegex;
private Regex? _filterRegex;
public event EventHandler RefreshFilter;
public event EventHandler? RefreshFilter;
public bool HasErrors => _errors.Count > 0;
public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
public bool Filter(string input)
{
@ -31,13 +34,11 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
_processedFilter = FilterString.Trim();
try
{
var options = RegexOptions.Compiled;
var pattern = UseRegexFilter
? _processedFilter : Regex.Escape(_processedFilter);
? FilterString.Trim() : Regex.Escape(FilterString.Trim());
if (!UseCaseSensitiveFilter)
{
options |= RegexOptions.IgnoreCase;
@ -116,9 +117,5 @@ namespace Avalonia.Diagnostics.ViewModels
yield return error;
}
}
public bool HasErrors => _errors.Count > 0;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
}

15
src/Avalonia.Diagnostics/Diagnostics/ViewModels/FiredEvent.cs

@ -8,15 +8,12 @@ namespace Avalonia.Diagnostics.ViewModels
internal class FiredEvent : ViewModelBase
{
private readonly RoutedEventArgs _eventArgs;
private EventChainLink _handledBy;
private EventChainLink? _handledBy;
public FiredEvent(RoutedEventArgs eventArgs, EventChainLink originator)
{
Contract.Requires<ArgumentNullException>(eventArgs != null);
Contract.Requires<ArgumentNullException>(originator != null);
_eventArgs = eventArgs;
Originator = originator;
_eventArgs = eventArgs ?? throw new ArgumentNullException(nameof(eventArgs));
Originator = originator ?? throw new ArgumentNullException(nameof(originator));
AddToChain(originator);
}
@ -25,7 +22,7 @@ namespace Avalonia.Diagnostics.ViewModels
return e == _eventArgs;
}
public RoutedEvent Event => _eventArgs.RoutedEvent;
public RoutedEvent Event => _eventArgs.RoutedEvent!;
public bool IsHandled => HandledBy?.Handled == true;
@ -38,7 +35,7 @@ namespace Avalonia.Diagnostics.ViewModels
if (IsHandled)
{
return $"{Event.Name} on {Originator.HandlerName};" + Environment.NewLine +
$"strategies: {Event.RoutingStrategies}; handled by: {HandledBy.HandlerName}";
$"strategies: {Event.RoutingStrategies}; handled by: {HandledBy!.HandlerName}";
}
return $"{Event.Name} on {Originator.HandlerName}; strategies: {Event.RoutingStrategies}";
@ -47,7 +44,7 @@ namespace Avalonia.Diagnostics.ViewModels
public EventChainLink Originator { get; }
public EventChainLink HandledBy
public EventChainLink? HandledBy
{
get => _handledBy;
set

8
src/Avalonia.Diagnostics/Diagnostics/ViewModels/LogicalTreeNode.cs

@ -7,22 +7,24 @@ namespace Avalonia.Diagnostics.ViewModels
{
internal class LogicalTreeNode : TreeNode
{
public LogicalTreeNode(ILogical logical, TreeNode parent)
public LogicalTreeNode(ILogical logical, TreeNode? parent)
: base((Control)logical, parent)
{
Children = new LogicalTreeNodeCollection(this, logical);
}
public override TreeNodeCollection Children { get; }
public static LogicalTreeNode[] Create(object control)
{
var logical = control as ILogical;
return logical != null ? new[] { new LogicalTreeNode(logical, null) } : null;
return logical != null ? new[] { new LogicalTreeNode(logical, null) } : Array.Empty<LogicalTreeNode>();
}
internal class LogicalTreeNodeCollection : TreeNodeCollection
{
private readonly ILogical _control;
private IDisposable _subscription;
private IDisposable? _subscription;
public LogicalTreeNodeCollection(TreeNode owner, ILogical control)
: base(owner)

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

@ -1,10 +1,11 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls;
using Avalonia.Diagnostics.Models;
using Avalonia.Input;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
{
@ -17,8 +18,8 @@ namespace Avalonia.Diagnostics.ViewModels
private readonly IDisposable _pointerOverSubscription;
private ViewModelBase _content;
private int _selectedTab;
private string _focusedControl;
private string _pointerOverElement;
private string? _focusedControl;
private string? _pointerOverElement;
private bool _shouldVisualizeMarginPadding = true;
private bool _shouldVisualizeDirtyRects;
private bool _showFpsOverlay;
@ -84,6 +85,7 @@ namespace Avalonia.Diagnostics.ViewModels
public ViewModelBase Content
{
get { return _content; }
[MemberNotNull(nameof(_content))]
private set
{
if (_content is TreePageViewModel oldTree &&
@ -114,34 +116,35 @@ namespace Avalonia.Diagnostics.ViewModels
public int SelectedTab
{
get { return _selectedTab; }
[MemberNotNull(nameof(_content))]
set
{
_selectedTab = value;
switch (value)
{
case 0:
Content = _logicalTree;
break;
case 1:
Content = _visualTree;
break;
case 2:
Content = _events;
break;
default:
Content = _logicalTree;
break;
}
RaisePropertyChanged();
}
}
public string FocusedControl
public string? FocusedControl
{
get { return _focusedControl; }
private set { RaiseAndSetIfChanged(ref _focusedControl, value); }
}
public string PointerOverElement
public string? PointerOverElement
{
get { return _pointerOverElement; }
private set { RaiseAndSetIfChanged(ref _pointerOverElement, value); }

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

@ -18,7 +18,7 @@ namespace Avalonia.Diagnostics.ViewModels
public abstract string Value { get; set; }
public abstract void Update();
protected static string ConvertToString(object value)
protected static string ConvertToString(object? value)
{
if (value is null)
{

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

@ -8,7 +8,7 @@ namespace Avalonia.Diagnostics.ViewModels
public IBrush Tint { get; }
public ResourceSetterViewModel(AvaloniaProperty property, object resourceKey, object resourceValue, bool isDynamic) : base(property, resourceValue)
public ResourceSetterViewModel(AvaloniaProperty property, object resourceKey, object? resourceValue, bool isDynamic) : base(property, resourceValue)
{
Key = resourceKey;
Tint = isDynamic ? Brushes.Orange : Brushes.Brown;

4
src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs

@ -11,7 +11,7 @@ namespace Avalonia.Diagnostics.ViewModels
public string Name { get; }
public object Value { get; }
public object? Value { get; }
public bool IsActive
{
@ -25,7 +25,7 @@ namespace Avalonia.Diagnostics.ViewModels
set => RaiseAndSetIfChanged(ref _isVisible, value);
}
public SetterViewModel(AvaloniaProperty property, object value)
public SetterViewModel(AvaloniaProperty property, object? value)
{
Property = property;
Name = property.Name;

16
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs

@ -9,17 +9,18 @@ using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
{
internal class TreeNode : ViewModelBase, IDisposable
internal abstract class TreeNode : ViewModelBase, IDisposable
{
private IDisposable _classesSubscription;
private IDisposable? _classesSubscription;
private string _classes;
private bool _isExpanded;
public TreeNode(IVisual visual, TreeNode parent)
public TreeNode(IVisual visual, TreeNode? parent)
{
Parent = parent;
Type = visual.GetType().Name;
Visual = visual;
_classes = string.Empty;
if (visual is IControl control)
{
@ -51,10 +52,9 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
public TreeNodeCollection Children
public abstract TreeNodeCollection Children
{
get;
protected set;
}
public string Classes
@ -63,7 +63,7 @@ namespace Avalonia.Diagnostics.ViewModels
private set { RaiseAndSetIfChanged(ref _classes, value); }
}
public string ElementName
public string? ElementName
{
get;
}
@ -79,7 +79,7 @@ namespace Avalonia.Diagnostics.ViewModels
set { RaiseAndSetIfChanged(ref _isExpanded, value); }
}
public TreeNode Parent
public TreeNode? Parent
{
get;
}
@ -92,7 +92,7 @@ namespace Avalonia.Diagnostics.ViewModels
public void Dispose()
{
_classesSubscription.Dispose();
_classesSubscription?.Dispose();
Children.Dispose();
}

29
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNodeCollection.cs

@ -3,13 +3,15 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Collections;
namespace Avalonia.Diagnostics.ViewModels
{
internal abstract class TreeNodeCollection : IAvaloniaReadOnlyList<TreeNode>, IDisposable
{
private AvaloniaList<TreeNode> _inner;
private AvaloniaList<TreeNode>? _inner;
public TreeNodeCollection(TreeNode owner) => Owner = owner;
@ -35,14 +37,30 @@ namespace Avalonia.Diagnostics.ViewModels
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add => _inner.CollectionChanged += value;
remove => _inner.CollectionChanged -= value;
add
{
EnsureInitialized();
_inner.CollectionChanged += value;
}
remove
{
EnsureInitialized();
_inner.CollectionChanged -= value;
}
}
public event PropertyChangedEventHandler PropertyChanged
{
add => _inner.PropertyChanged += value;
remove => _inner.PropertyChanged -= value;
add
{
EnsureInitialized();
_inner.PropertyChanged += value;
}
remove
{
EnsureInitialized();
_inner.PropertyChanged -= value;
}
}
public virtual void Dispose()
@ -66,6 +84,7 @@ namespace Avalonia.Diagnostics.ViewModels
protected abstract void Initialize(AvaloniaList<TreeNode> nodes);
[MemberNotNull(nameof(_inner))]
private void EnsureInitialized()
{
if (_inner is null)

14
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs

@ -6,8 +6,8 @@ namespace Avalonia.Diagnostics.ViewModels
{
internal class TreePageViewModel : ViewModelBase, IDisposable
{
private TreeNode _selectedNode;
private ControlDetailsViewModel _details;
private TreeNode? _selectedNode;
private ControlDetailsViewModel? _details;
public TreePageViewModel(MainViewModel mainView, TreeNode[] nodes)
{
@ -29,7 +29,7 @@ namespace Avalonia.Diagnostics.ViewModels
public TreeNode[] Nodes { get; protected set; }
public TreeNode SelectedNode
public TreeNode? SelectedNode
{
get => _selectedNode;
private set
@ -44,7 +44,7 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
public ControlDetailsViewModel Details
public ControlDetailsViewModel? Details
{
get => _details;
private set
@ -68,7 +68,7 @@ namespace Avalonia.Diagnostics.ViewModels
_details?.Dispose();
}
public TreeNode FindNode(IControl control)
public TreeNode? FindNode(IControl control)
{
foreach (var node in Nodes)
{
@ -104,7 +104,7 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
private void ExpandNode(TreeNode node)
private void ExpandNode(TreeNode? node)
{
if (node != null)
{
@ -113,7 +113,7 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
private TreeNode FindNode(TreeNode node, IControl control)
private TreeNode? FindNode(TreeNode node, IControl control)
{
if (node.Visual == control)
{

10
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ViewModelBase.cs

@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace Avalonia.Diagnostics.ViewModels
{
internal class ViewModelBase : INotifyPropertyChanged
{
private PropertyChangedEventHandler _propertyChanged;
private PropertyChangedEventHandler? _propertyChanged;
private List<string> events = new List<string>();
public event PropertyChangedEventHandler PropertyChanged
@ -20,7 +20,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
}
protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
protected bool RaiseAndSetIfChanged<T>([NotNullIfNotNull("value")] ref T field, T value, [CallerMemberName] string propertyName = null!)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
@ -32,7 +32,7 @@ namespace Avalonia.Diagnostics.ViewModels
return false;
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null!)
{
var e = new PropertyChangedEventArgs(propertyName);
OnPropertyChanged(e);

8
src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs

@ -7,7 +7,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
internal class VisualTreeNode : TreeNode
{
public VisualTreeNode(IVisual visual, TreeNode parent)
public VisualTreeNode(IVisual visual, TreeNode? parent)
: base(visual, parent)
{
Children = new VisualTreeNodeCollection(this, visual);
@ -20,16 +20,18 @@ namespace Avalonia.Diagnostics.ViewModels
public bool IsInTemplate { get; private set; }
public override TreeNodeCollection Children { get; }
public static VisualTreeNode[] Create(object control)
{
var visual = control as IVisual;
return visual != null ? new[] { new VisualTreeNode(visual, null) } : null;
return visual != null ? new[] { new VisualTreeNode(visual, null) } : Array.Empty<VisualTreeNode>();
}
internal class VisualTreeNodeCollection : TreeNodeCollection
{
private readonly IVisual _control;
private IDisposable _subscription;
private IDisposable? _subscription;
public VisualTreeNodeCollection(TreeNode owner, IVisual control)
: base(owner)

8
src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml.cs

@ -40,12 +40,16 @@ namespace Avalonia.Diagnostics.Views
private void InputKeyDown(object sender, KeyEventArgs e)
{
var vm = (ConsoleViewModel)DataContext;
var vm = (ConsoleViewModel?)DataContext;
if (vm is null)
{
return;
}
switch (e.Key)
{
case Key.Enter:
vm.Execute();
_ = vm.Execute();
e.Handled = true;
break;
case Key.Up:

6
src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml.cs

@ -27,7 +27,11 @@ namespace Avalonia.Diagnostics.Views
public void ToggleConsole()
{
var vm = (MainViewModel)DataContext;
var vm = (MainViewModel?)DataContext;
if (vm is null)
{
return;
}
if (_consoleHeight == -1)
{

31
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs

@ -15,7 +15,7 @@ namespace Avalonia.Diagnostics.Views
internal class MainWindow : Window, IStyleHost
{
private readonly IDisposable _keySubscription;
private TopLevel _root;
private TopLevel? _root;
public MainWindow()
{
@ -26,7 +26,7 @@ namespace Avalonia.Diagnostics.Views
.Subscribe(RawKeyDown);
}
public TopLevel Root
public TopLevel? Root
{
get => _root;
set
@ -43,7 +43,7 @@ namespace Avalonia.Diagnostics.Views
if (_root != null)
{
_root.Closed += RootClosed;
DataContext = new MainViewModel(value);
DataContext = new MainViewModel(_root);
}
else
{
@ -53,15 +53,20 @@ namespace Avalonia.Diagnostics.Views
}
}
IStyleHost IStyleHost.StylingParent => null;
IStyleHost? IStyleHost.StylingParent => null;
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
_keySubscription.Dispose();
_root.Closed -= RootClosed;
_root = null;
((MainViewModel)DataContext)?.Dispose();
if (_root != null)
{
_root.Closed -= RootClosed;
_root = null;
}
((MainViewModel?)DataContext)?.Dispose();
}
private void InitializeComponent()
@ -71,12 +76,20 @@ namespace Avalonia.Diagnostics.Views
private void RawKeyDown(RawKeyEventArgs e)
{
var vm = (MainViewModel?)DataContext;
if (vm is null)
{
return;
}
const RawInputModifiers modifiers = RawInputModifiers.Control | RawInputModifiers.Shift;
if (e.Modifiers == modifiers)
{
#pragma warning disable CS0618 // Type or member is obsolete
var point = (Root as IInputRoot)?.MouseDevice?.GetPosition(Root) ?? default;
#pragma warning restore CS0618 // Type or member is obsolete
var control = Root.GetVisualsAt(point, x =>
{
if (x is AdornerLayer || !x.IsVisible) return false;
@ -87,7 +100,6 @@ namespace Avalonia.Diagnostics.Views
if (control != null)
{
var vm = (MainViewModel)DataContext;
vm.SelectControl((IControl)control);
}
}
@ -97,7 +109,6 @@ namespace Avalonia.Diagnostics.Views
{
var enable = e.Key == Key.S;
var vm = (MainViewModel)DataContext;
vm.EnableSnapshotStyles(enable);
}
}

14
src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs

@ -14,12 +14,13 @@ namespace Avalonia.Diagnostics.Views
internal class TreePageView : UserControl
{
private readonly Panel _adorner;
private AdornerLayer _currentLayer;
private AdornerLayer? _currentLayer;
private TreeView _tree;
public TreePageView()
{
InitializeComponent();
_tree = this.FindControl<TreeView>("tree");
_tree.ItemContainerGenerator.Index.Materialized += TreeViewItemMaterialized;
_adorner = new Panel
@ -39,7 +40,13 @@ namespace Avalonia.Diagnostics.Views
protected void AddAdorner(object sender, PointerEventArgs e)
{
var node = (TreeNode)((Control)sender).DataContext;
var node = (TreeNode?)((Control)sender).DataContext;
var vm = (TreePageViewModel?)DataContext;
if (node is null || vm is null)
{
return;
}
var visual = (Visual)node.Visual;
_currentLayer = AdornerLayer.GetAdornerLayer(visual);
@ -53,8 +60,6 @@ namespace Avalonia.Diagnostics.Views
_currentLayer.Children.Add(_adorner);
AdornerLayer.SetAdornedElement(_adorner, visual);
var vm = (TreePageViewModel) DataContext;
if (vm.MainView.ShouldVisualizeMarginPadding)
{
var paddingBorder = (Border)_adorner.Children[0];
@ -90,7 +95,6 @@ namespace Avalonia.Diagnostics.Views
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
_tree = this.FindControl<TreeView>("tree");
}
private void TreeViewItemMaterialized(object sender, ItemContainerEventArgs e)

2
src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs

@ -17,7 +17,7 @@ namespace Avalonia.Diagnostics
private static void PrintVisualTree(IVisual visual, StringBuilder builder, int indent)
{
Control control = visual as Control;
Control? control = visual as Control;
builder.Append(Indent(indent - 1));

Loading…
Cancel
Save