Browse Source

feat(DevTools): Add to Events View visual feedback (#14467)

pull/14627/head
workgroupengineering 3 years ago
committed by GitHub
parent
commit
47700d2856
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 59
      src/Avalonia.Diagnostics/Diagnostics/Controls/ControlHighlightAdorner.cs
  2. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventsPageViewModel.cs
  3. 10
      src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml
  4. 17
      src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml.cs
  5. 92
      src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs

59
src/Avalonia.Diagnostics/Diagnostics/Controls/ControlHighlightAdorner.cs

@ -9,7 +9,24 @@ namespace Avalonia.Diagnostics.Controls;
internal class ControlHighlightAdorner : Control
{
private static readonly Panel _layoutHighlightAdorner;
static ControlHighlightAdorner()
{
_layoutHighlightAdorner = new Panel
{
ClipToBounds = false,
Children =
{
//Padding frame
new Border { BorderBrush = new SolidColorBrush(Colors.Green, 0.5) },
//Content frame
new Border { Background = new SolidColorBrush(Color.FromRgb(160, 197, 232), 0.5) },
//Margin frame
new Border { BorderBrush = new SolidColorBrush(Colors.Yellow, 0.5) }
},
};
AdornerLayer.SetIsClipEnabled(_layoutHighlightAdorner, false);
}
readonly IPen _pen;
private ControlHighlightAdorner(IPen pen)
@ -44,4 +61,44 @@ internal class ControlHighlightAdorner : Control
context.DrawRectangle(_pen, Bounds.Deflate(2));
}
internal static IDisposable? Add(Visual visual, bool visualizeMarginPadding)
{
if (AdornerLayer.GetAdornerLayer(visual) is { } layer)
{
if (layer.Children.Contains(_layoutHighlightAdorner))
{
return default;
}
layer.Children.Add(_layoutHighlightAdorner);
AdornerLayer.SetAdornedElement(_layoutHighlightAdorner, visual);
var paddingBorder = (Border)_layoutHighlightAdorner.Children[0];
var contentBorder = (Border)_layoutHighlightAdorner.Children[1];
var marginBorder = (Border)_layoutHighlightAdorner.Children[2];
if (visualizeMarginPadding)
{
paddingBorder.BorderThickness = visual.GetValue(TemplatedControl.PaddingProperty);
contentBorder.Margin = visual.GetValue(TemplatedControl.PaddingProperty);
marginBorder.BorderThickness = visual.GetValue(MarginProperty);
marginBorder.Margin = InvertThickness(visual.GetValue(TemplatedControl.MarginProperty));
}
else
{
paddingBorder.BorderThickness = default;
contentBorder.Margin = default;
marginBorder.BorderThickness = default;
marginBorder.Margin = default;
}
return Disposable.Create((Layer: layer, Adorner: _layoutHighlightAdorner), state =>
{
state.Layer.Children.Remove(state.Adorner);
});
}
return default;
}
private static Thickness InvertThickness(Thickness input)
{
return new Thickness(-input.Left, -input.Top, -input.Right, -input.Bottom);
}
}

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

@ -171,5 +171,7 @@ namespace Avalonia.Diagnostics.ViewModels
return node.IsVisible;
}
}
public MainViewModel MainView => _mainViewModel;
}
}

10
src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml

@ -113,14 +113,20 @@
<ListBox ItemsSource="{Binding SelectedEvent.EventChain}">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem Classes.handled="{Binding Handled}">
<ListBoxItem Classes.handled="{Binding Handled}"
PointerEntered="ListBoxItem_PointerEntered"
PointerExited="ListBoxItem_PointerExited"
>
<StackPanel Orientation="Vertical">
<Rectangle IsVisible="{Binding BeginsNewRoute}" StrokeDashArray="2,2" StrokeThickness="1" Stroke="Gray" />
<StackPanel Orientation="Horizontal" Spacing="2">
<TextBlock Text="{Binding Route}" FontWeight="Bold" />
<TextBlock Tag="{Binding}" DoubleTapped="NavigateTo" Text="{Binding HandlerName}" Classes="nav" />
<TextBlock Tag="{Binding}"
DoubleTapped="NavigateTo"
Text="{Binding HandlerName}"
Classes="nav" />
</StackPanel>
</StackPanel>

17
src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml.cs

@ -15,6 +15,7 @@ namespace Avalonia.Diagnostics.Views
internal class EventsPageView : UserControl
{
private readonly ListBox _events;
private IDisposable? _adorner;
public EventsPageView()
{
@ -72,5 +73,21 @@ namespace Avalonia.Diagnostics.Views
{
AvaloniaXamlLoader.Load(this);
}
private void ListBoxItem_PointerEntered(object? sender, PointerEventArgs e)
{
if (DataContext is EventsPageViewModel vm
&& sender is ListBoxItem control
&& control.DataContext is EventChainLink chainLink
&& chainLink.Handler is Visual visual)
{
_adorner = Controls.ControlHighlightAdorner.Add(visual, vm.MainView.ShouldVisualizeMarginPadding);
}
}
private void ListBoxItem_PointerExited(object? sender, PointerEventArgs e)
{
_adorner?.Dispose();
}
}
}

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

@ -1,99 +1,21 @@
using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace Avalonia.Diagnostics.Views
{
internal class TreePageView : UserControl
{
private readonly Panel _adorner;
private AdornerLayer? _currentLayer;
private TreeViewItem? _hovered;
private TreeView _tree;
private System.IDisposable? _adorner;
public TreePageView()
{
InitializeComponent();
_tree = this.GetControl<TreeView>("tree");
_adorner = new Panel
{
ClipToBounds = false,
Children =
{
//Padding frame
new Border { BorderBrush = new SolidColorBrush(Colors.Green, 0.5) },
//Content frame
new Border { Background = new SolidColorBrush(Color.FromRgb(160, 197, 232), 0.5) },
//Margin frame
new Border { BorderBrush = new SolidColorBrush(Colors.Yellow, 0.5) }
},
};
AdornerLayer.SetIsClipEnabled(_adorner, false);
}
private static Thickness InvertThickness(Thickness input)
{
return new Thickness(-input.Left, -input.Top, -input.Right, -input.Bottom);
}
protected void AddAdorner(object? sender, PointerEventArgs e)
{
var node = (TreeNode?)((Control)sender!).DataContext;
var vm = (TreePageViewModel?)DataContext;
if (node is null || vm is null)
{
return;
}
var visual = node.Visual as Visual;
if (visual is null)
{
return;
}
_currentLayer = AdornerLayer.GetAdornerLayer(visual);
if (_currentLayer == null ||
_currentLayer.Children.Contains(_adorner))
{
return;
}
_currentLayer.Children.Add(_adorner);
AdornerLayer.SetAdornedElement(_adorner, visual);
if (vm.MainView.ShouldVisualizeMarginPadding)
{
var paddingBorder = (Border)_adorner.Children[0];
paddingBorder.BorderThickness = visual.GetValue(PaddingProperty);
var contentBorder = (Border)_adorner.Children[1];
contentBorder.Margin = visual.GetValue(PaddingProperty);
var marginBorder = (Border)_adorner.Children[2];
marginBorder.BorderThickness = visual.GetValue(MarginProperty);
marginBorder.Margin = InvertThickness(visual.GetValue(MarginProperty));
}
}
protected void RemoveAdorner(object? sender, PointerEventArgs e)
{
foreach (var border in _adorner.Children.OfType<Border>())
{
border.Margin = default;
border.Padding = default;
border.BorderThickness = default;
}
_currentLayer?.Children.Remove(_adorner);
_currentLayer = null;
}
protected void UpdateAdorner(object? sender, PointerEventArgs e)
@ -109,7 +31,7 @@ namespace Avalonia.Diagnostics.Views
return;
}
RemoveAdorner(sender, e);
_adorner?.Dispose();
if (item is null || item.TreeViewOwner != _tree)
{
@ -118,7 +40,15 @@ namespace Avalonia.Diagnostics.Views
}
_hovered = item;
AddAdorner(item, e);
var visual = (item.DataContext as TreeNode)?.Visual as Visual;
var shouldVisualizeMarginPadding = (DataContext as TreePageViewModel)?.MainView.ShouldVisualizeMarginPadding;
if (visual is null || shouldVisualizeMarginPadding is null)
{
return;
}
_adorner = Controls.ControlHighlightAdorner.Add(visual, visualizeMarginPadding: shouldVisualizeMarginPadding == true);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)

Loading…
Cancel
Save