diff --git a/src/Avalonia.Diagnostics/Models/ChainLink.cs b/src/Avalonia.Diagnostics/Models/EventChainLink.cs similarity index 59% rename from src/Avalonia.Diagnostics/Models/ChainLink.cs rename to src/Avalonia.Diagnostics/Models/EventChainLink.cs index 9ea99ff1ae..aab50a13dd 100644 --- a/src/Avalonia.Diagnostics/Models/ChainLink.cs +++ b/src/Avalonia.Diagnostics/Models/EventChainLink.cs @@ -1,13 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; using Avalonia.Interactivity; namespace Avalonia.Diagnostics.Models { - internal class ChainLink + internal class EventChainLink { - public object Handler { get; private set; } + public EventChainLink(object handler, bool handled, RoutingStrategies route) + { + Contract.Requires(handler != null); + + this.Handler = handler; + this.Handled = handled; + this.Route = route; + } + + public object Handler { get; } + public string HandlerName { get @@ -19,16 +30,9 @@ namespace Avalonia.Diagnostics.Models return Handler.GetType().Name; } } - public bool Handled { get; private set; } - public RoutingStrategies Route { get; private set; } - public ChainLink(object handler, bool handled, RoutingStrategies route) - { - Contract.Requires(handler != null); + public bool Handled { get; } - this.Handler = handler; - this.Handled = handled; - this.Route = route; - } + public RoutingStrategies Route { get; } } } diff --git a/src/Avalonia.Diagnostics/ViewModels/EventEntryTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/EventEntryTreeNode.cs deleted file mode 100644 index 8eead869d5..0000000000 --- a/src/Avalonia.Diagnostics/ViewModels/EventEntryTreeNode.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) The Avalonia Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. - -using System; -using Avalonia.Diagnostics.Models; -using Avalonia.Interactivity; -using Avalonia.Threading; -using Avalonia.VisualTree; - -namespace Avalonia.Diagnostics.ViewModels -{ - internal class EventEntryTreeNode : EventTreeNode - { - RoutedEvent _event; - EventsViewModel _parentViewModel; - bool _isRegistered; - FiredEvent _currentEvent; - - public EventEntryTreeNode(ControlTreeNode parent, RoutedEvent @event, EventsViewModel vm) - : base(parent, @event.Name) - { - Contract.Requires(@event != null); - Contract.Requires(vm != null); - - this._event = @event; - this._parentViewModel = vm; - } - - public override bool? IsEnabled - { - get => base.IsEnabled; - set - { - if (base.IsEnabled != value) - { - base.IsEnabled = value; - UpdateTracker(); - if (Parent != null && _updateParent) - { - try - { - Parent._updateChildren = false; - Parent.UpdateChecked(); - } - finally - { - Parent._updateChildren = true; - } - } - } - } - } - - private void UpdateTracker() - { - if (IsEnabled.GetValueOrDefault() && !_isRegistered) - { - _event.AddClassHandler(typeof(object), HandleEvent, (RoutingStrategies)7, handledEventsToo: true); - _isRegistered = true; - } - } - - private void HandleEvent(object sender, RoutedEventArgs e) - { - if (!_isRegistered || IsEnabled == false) - return; - if (sender is IVisual v && DevTools.BelongsToDevTool(v)) - return; - - var s = sender; - var handled = e.Handled; - var route = e.Route; - - Action handler = delegate - { - if (_currentEvent == null || !_currentEvent.IsPartOfSameEventChain(e)) - { - _currentEvent = new FiredEvent(e, new ChainLink(s, handled, route)); - - _parentViewModel.RecordedEvents.Add(_currentEvent); - - while (_parentViewModel.RecordedEvents.Count > 100) - _parentViewModel.RecordedEvents.RemoveAt(0); - } - else - { - _currentEvent.AddToChain(new ChainLink(s, handled, route)); - } - }; - - if (!Dispatcher.UIThread.CheckAccess()) - Dispatcher.UIThread.Post(handler); - else - handler(); - } - } -} diff --git a/src/Avalonia.Diagnostics/ViewModels/ControlTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs similarity index 76% rename from src/Avalonia.Diagnostics/ViewModels/ControlTreeNode.cs rename to src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs index d8a6a3bdc3..0674918400 100644 --- a/src/Avalonia.Diagnostics/ViewModels/ControlTreeNode.cs +++ b/src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs @@ -11,16 +11,9 @@ using Avalonia.Interactivity; namespace Avalonia.Diagnostics.ViewModels { - internal class ControlTreeNode : EventTreeNode + internal class EventOwnerTreeNode : EventTreeNodeBase { - public ControlTreeNode(Type type, IEnumerable events, EventsViewModel vm) - : base(null, type.Name) - { - this.Children = new AvaloniaList(events.OrderBy(e => e.Name).Select(e => new EventEntryTreeNode(this, e, vm) { IsEnabled = IsDefault(e) })); - this.IsExpanded = true; - } - - RoutedEvent[] defaultEvents = new RoutedEvent[] + private static readonly RoutedEvent[] s_defaultEvents = new RoutedEvent[] { Button.ClickEvent, InputElement.KeyDownEvent, @@ -30,9 +23,12 @@ namespace Avalonia.Diagnostics.ViewModels InputElement.PointerPressedEvent, }; - private bool IsDefault(RoutedEvent e) + public EventOwnerTreeNode(Type type, IEnumerable events, EventsViewModel vm) + : base(null, type.Name) { - return defaultEvents.Contains(e); + this.Children = new AvaloniaList(events.OrderBy(e => e.Name) + .Select(e => new EventTreeNode(this, e, vm) { IsEnabled = s_defaultEvents.Contains(e) })); + this.IsExpanded = true; } public override bool? IsEnabled diff --git a/src/Avalonia.Diagnostics/ViewModels/EventTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/EventTreeNode.cs index 50ea0c9c31..59c0f82414 100644 --- a/src/Avalonia.Diagnostics/ViewModels/EventTreeNode.cs +++ b/src/Avalonia.Diagnostics/ViewModels/EventTreeNode.cs @@ -1,80 +1,98 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System.Diagnostics; -using Avalonia.Collections; +using System; + +using Avalonia.Diagnostics.Models; +using Avalonia.Interactivity; +using Avalonia.Threading; using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels { - internal abstract class EventTreeNode : ViewModelBase + internal class EventTreeNode : EventTreeNodeBase { - internal bool _updateChildren = true; - internal bool _updateParent = true; - private bool _isExpanded; - private bool? _isEnabled = false; - - public EventTreeNode(EventTreeNode parent, string text) - { - this.Parent = parent; - this.Text = text; - } + private RoutedEvent _event; + private EventsViewModel _parentViewModel; + private bool _isRegistered; + private FiredEvent _currentEvent; - public IAvaloniaReadOnlyList Children + public EventTreeNode(EventOwnerTreeNode parent, RoutedEvent @event, EventsViewModel vm) + : base(parent, @event.Name) { - get; - protected set; - } + Contract.Requires(@event != null); + Contract.Requires(vm != null); - public bool IsExpanded - { - get { return _isExpanded; } - set { RaiseAndSetIfChanged(ref _isExpanded, value); } + this._event = @event; + this._parentViewModel = vm; } - public virtual bool? IsEnabled + public override bool? IsEnabled { - get { return _isEnabled; } - set { RaiseAndSetIfChanged(ref _isEnabled, value); } + get => base.IsEnabled; + set + { + if (base.IsEnabled != value) + { + base.IsEnabled = value; + UpdateTracker(); + if (Parent != null && _updateParent) + { + try + { + Parent._updateChildren = false; + Parent.UpdateChecked(); + } + finally + { + Parent._updateChildren = true; + } + } + } + } } - public EventTreeNode Parent + private void UpdateTracker() { - get; + if (IsEnabled.GetValueOrDefault() && !_isRegistered) + { + _event.AddClassHandler(typeof(object), HandleEvent, (RoutingStrategies)7, handledEventsToo: true); + _isRegistered = true; + } } - public string Text + private void HandleEvent(object sender, RoutedEventArgs e) { - get; - private set; - } + if (!_isRegistered || IsEnabled == false) + return; + if (sender is IVisual v && DevTools.BelongsToDevTool(v)) + return; - internal void UpdateChecked() - { - IsEnabled = GetValue(); + var s = sender; + var handled = e.Handled; + var route = e.Route; - bool? GetValue() + Action handler = delegate { - if (Children == null) - return false; - bool? value = false; - for (int i = 0; i < Children.Count; i++) + if (_currentEvent == null || !_currentEvent.IsPartOfSameEventChain(e)) { - if (i == 0) - { - value = Children[i].IsEnabled; - continue; - } + _currentEvent = new FiredEvent(e, new EventChainLink(s, handled, route)); - if (value != Children[i].IsEnabled) - { - value = null; - break; - } + _parentViewModel.RecordedEvents.Add(_currentEvent); + + while (_parentViewModel.RecordedEvents.Count > 100) + _parentViewModel.RecordedEvents.RemoveAt(0); } + else + { + _currentEvent.AddToChain(new EventChainLink(s, handled, route)); + } + }; - return value; - } + if (!Dispatcher.UIThread.CheckAccess()) + Dispatcher.UIThread.Post(handler); + else + handler(); } } } diff --git a/src/Avalonia.Diagnostics/ViewModels/EventTreeNodeBase.cs b/src/Avalonia.Diagnostics/ViewModels/EventTreeNodeBase.cs new file mode 100644 index 0000000000..146a8cea8e --- /dev/null +++ b/src/Avalonia.Diagnostics/ViewModels/EventTreeNodeBase.cs @@ -0,0 +1,78 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Avalonia.Collections; + +namespace Avalonia.Diagnostics.ViewModels +{ + internal abstract class EventTreeNodeBase : ViewModelBase + { + internal bool _updateChildren = true; + internal bool _updateParent = true; + private bool _isExpanded; + private bool? _isEnabled = false; + + public EventTreeNodeBase(EventTreeNodeBase parent, string text) + { + this.Parent = parent; + this.Text = text; + } + + public IAvaloniaReadOnlyList Children + { + get; + protected set; + } + + public bool IsExpanded + { + get { return _isExpanded; } + set { RaiseAndSetIfChanged(ref _isExpanded, value); } + } + + public virtual bool? IsEnabled + { + get { return _isEnabled; } + set { RaiseAndSetIfChanged(ref _isEnabled, value); } + } + + public EventTreeNodeBase Parent + { + get; + } + + public string Text + { + get; + private set; + } + + internal void UpdateChecked() + { + IsEnabled = GetValue(); + + bool? GetValue() + { + if (Children == null) + return false; + bool? value = false; + for (int i = 0; i < Children.Count; i++) + { + if (i == 0) + { + value = Children[i].IsEnabled; + continue; + } + + if (value != Children[i].IsEnabled) + { + value = null; + break; + } + } + + return value; + } + } + } +} diff --git a/src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs index a9a6334689..a23677afc8 100644 --- a/src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/EventsViewModel.cs @@ -2,26 +2,22 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; -using System.Text; using System.Windows.Input; -using Avalonia.Collections; + using Avalonia.Controls; using Avalonia.Data.Converters; using Avalonia.Interactivity; using Avalonia.Media; -using Avalonia.Threading; namespace Avalonia.Diagnostics.ViewModels { internal class EventsViewModel : ViewModelBase { - private IControl _root; + private readonly IControl _root; private FiredEvent _selectedEvent; - private ICommand ClearCommand { get; } public EventsViewModel(IControl root) { @@ -29,27 +25,11 @@ namespace Avalonia.Diagnostics.ViewModels this.Nodes = RoutedEventRegistry.Instance.GetAllRegistered() .GroupBy(e => e.OwnerType) .OrderBy(e => e.Key.Name) - .Select(g => new ControlTreeNode(g.Key, g, this)) + .Select(g => new EventOwnerTreeNode(g.Key, g, this)) .ToArray(); } - private void ClearExecute() - { - Action action = delegate - { - RecordedEvents.Clear(); - }; - if (!Dispatcher.UIThread.CheckAccess()) - { - Dispatcher.UIThread.Post(action); - } - else - { - action(); - } - } - - public EventTreeNode[] Nodes { get; } + public EventTreeNodeBase[] Nodes { get; } public ObservableCollection RecordedEvents { get; } = new ObservableCollection(); @@ -58,6 +38,11 @@ namespace Avalonia.Diagnostics.ViewModels get => _selectedEvent; set => RaiseAndSetIfChanged(ref _selectedEvent, value); } + + private void Clear() + { + RecordedEvents.Clear(); + } } internal class BoolToBrushConverter : IValueConverter diff --git a/src/Avalonia.Diagnostics/ViewModels/FiredEvent.cs b/src/Avalonia.Diagnostics/ViewModels/FiredEvent.cs index 523525b634..049280c390 100644 --- a/src/Avalonia.Diagnostics/ViewModels/FiredEvent.cs +++ b/src/Avalonia.Diagnostics/ViewModels/FiredEvent.cs @@ -3,6 +3,7 @@ using System; using System.Collections.ObjectModel; + using Avalonia.Diagnostics.Models; using Avalonia.Interactivity; @@ -11,17 +12,15 @@ namespace Avalonia.Diagnostics.ViewModels internal class FiredEvent : ViewModelBase { private RoutedEventArgs _eventArgs; + private EventChainLink _handledBy; - private ChainLink _handledBy; - private ChainLink _originator; - - public FiredEvent(RoutedEventArgs eventArgs, ChainLink originator) + public FiredEvent(RoutedEventArgs eventArgs, EventChainLink originator) { Contract.Requires(eventArgs != null); Contract.Requires(originator != null); this._eventArgs = eventArgs; - this._originator = originator; + this.Originator = originator; AddToChain(originator); } @@ -34,7 +33,7 @@ namespace Avalonia.Diagnostics.ViewModels public bool IsHandled => HandledBy?.Handled == true; - public ObservableCollection EventChain { get; } = new ObservableCollection(); + public ObservableCollection EventChain { get; } = new ObservableCollection(); public string DisplayText { @@ -49,21 +48,9 @@ namespace Avalonia.Diagnostics.ViewModels } } - public ChainLink Originator - { - get { return _originator; } - set - { - if (_originator != value) - { - _originator = value; - RaisePropertyChanged(); - RaisePropertyChanged(nameof(DisplayText)); - } - } - } + public EventChainLink Originator { get; } - public ChainLink HandledBy + public EventChainLink HandledBy { get { return _handledBy; } set @@ -80,10 +67,10 @@ namespace Avalonia.Diagnostics.ViewModels public void AddToChain(object handler, bool handled, RoutingStrategies route) { - AddToChain(new ChainLink(handler, handled, route)); + AddToChain(new EventChainLink(handler, handled, route)); } - public void AddToChain(ChainLink link) + public void AddToChain(EventChainLink link) { EventChain.Add(link); if (HandledBy == null && link.Handled) diff --git a/src/Avalonia.Diagnostics/Views/EventsView.xaml b/src/Avalonia.Diagnostics/Views/EventsView.xaml index 79f78d2bba..a5be86b613 100644 --- a/src/Avalonia.Diagnostics/Views/EventsView.xaml +++ b/src/Avalonia.Diagnostics/Views/EventsView.xaml @@ -7,7 +7,7 @@ - @@ -46,7 +46,7 @@ -