Bladeren bron

More pointer debug stuff

CPKreuz 1 jaar geleden
bovenliggende
commit
57a59b2550

+ 66 - 8
src/PixiEditor.AvaloniaUI/Views/Dialogs/Debugging/PointerDebugField.cs

@@ -9,17 +9,21 @@ namespace PixiEditor.AvaloniaUI.Views.Dialogs.Debugging;
 public class PointerDebugField : Control
 {
     private List<PointerPoint> _lastPoints = new();
-    private Pen pen = new(Brushes.Aqua);
+    
+    private Pen normalPen = new(Brushes.Aqua);
+    private Pen redPen = new(Brushes.Red);
+    private Pen yellowPen = new(Brushes.Yellow);
+    private Pen greenPen = new(Brushes.Lime);
 
     public static readonly StyledProperty<int> PointCountProperty =
         AvaloniaProperty.Register<PointerDebugPopup, int>(nameof(PointCount));
-
+    
     public int PointCount
     {
         get => GetValue(PointCountProperty);
         set => SetValue(PointCountProperty, value);
     }
-    
+
     public static readonly StyledProperty<IBrush> PointBrushProperty =
         AvaloniaProperty.Register<PointerDebugPopup, IBrush>(nameof(PointBrush));
 
@@ -29,6 +33,25 @@ public class PointerDebugField : Control
         set => SetValue(PointBrushProperty, value);
     }
 
+    public static readonly StyledProperty<PointerDebugPopup.ScrollStats> ScrollInfoProperty =
+        AvaloniaProperty.Register<PointerDebugPopup, PointerDebugPopup.ScrollStats>(nameof(ScrollInfo));
+
+    public PointerDebugPopup.ScrollStats ScrollInfo
+    {
+        get => GetValue(ScrollInfoProperty);
+        set => SetValue(ScrollInfoProperty, value);
+    }
+
+    static PointerDebugField()
+    {
+        ScrollInfoProperty.Changed.AddClassHandler<PointerDebugField>(OnChange);
+    }
+
+    private static void OnChange(PointerDebugField sender, AvaloniaPropertyChangedEventArgs args)
+    {
+        sender.InvalidateVisual();
+    }
+
     public void ReportPoint(PointerPoint point)
     {
         // if (_lastPoints.Count > 5000) _lastPoints.Clear();
@@ -51,13 +74,33 @@ public class PointerDebugField : Control
 
     public override void Render(DrawingContext context)
     {
-        int xCount = (int)(DesiredSize.Width / 15);
-        int yCount = (int)(DesiredSize.Height / 15);
-        for (int y = 0; y < yCount; y++)
+        var size = DesiredSize;
+
+        const double pointDistance = 15;
+        const double pointRadius = 2;
+        const double scrollSensitivity = 15;
+        
+        var xCount = (int)(size.Width / pointDistance);
+        var yCount = (int)(size.Height / pointDistance);
+
+        double xMidpoint = (size.Width - xCount * pointDistance) / 2d + pointDistance;
+        double yMidpoint = (size.Height - yCount * pointDistance) / 2d + pointDistance;
+
+        double xScroll = (ScrollInfo.TotalScroll.X % scrollSensitivity) * (pointDistance / scrollSensitivity);
+        double yScroll = (ScrollInfo.TotalScroll.Y % scrollSensitivity) * (pointDistance / scrollSensitivity);
+
+        double xOffset = xMidpoint + xScroll;
+        double yOffset = yMidpoint + yScroll;
+
+        context.PushClip(new RoundedRect(new Rect(size), 10));
+        
+        for (int y = -2; y < yCount + 2; y++)
         {
-            for (int x = 0; x < xCount; x++)
+            for (int x = -2; x < xCount + 2; x++)
             {
-                context.DrawEllipse(PointBrush, null, new Point(x * 15 + 15, y * 15 + 15), 2, 2);
+                var point = new Point(x * pointDistance + xOffset, y * pointDistance + yOffset);
+                
+                context.DrawEllipse(PointBrush, null, point, pointRadius, pointRadius);
             }
         }
         
@@ -65,6 +108,21 @@ public class PointerDebugField : Control
         {
             var point1 = _lastPoints[i];
             var point2 = _lastPoints[i + 1];
+
+            var pen = normalPen;
+            
+            if (point2.Properties.IsLeftButtonPressed)
+            {
+                pen = redPen;
+            }
+            else if (point2.Properties.IsRightButtonPressed)
+            {
+                pen = greenPen;
+            }
+            else if (point2.Properties.IsMiddleButtonPressed)
+            {
+                pen = yellowPen;
+            }
             
             context.DrawLine(pen, point1.Position, point2.Position);
         }

+ 103 - 10
src/PixiEditor.AvaloniaUI/Views/Dialogs/Debugging/PointerDebugPopup.axaml

@@ -5,8 +5,9 @@
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:dialogs="clr-namespace:PixiEditor.AvaloniaUI.Views.Dialogs"
     xmlns:debugging="clr-namespace:PixiEditor.AvaloniaUI.Views.Dialogs.Debugging"
+    xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
     mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
-    Width="1000" Height="800"
+    Width="1000" Height="900"
     x:Class="PixiEditor.AvaloniaUI.Views.Dialogs.Debugging.PointerDebugPopup"
     Name="uc"
     Title="Pointer Debug Window" Padding="10">
@@ -15,18 +16,110 @@
                 BorderBrush="{DynamicResource ThemeBorderHighBrush}"
                 Background="{DynamicResource ThemeControlLowBrush}"
                 PointerMoved="OnPointerMoved"
-                PointerEntered="OnPointerEntered">
-            <debugging:PointerDebugField Name="DebugField" PointBrush="{DynamicResource ThemeBorderHighBrush}" />
+                PointerEntered="OnPointerEntered"
+                PointerPressed="OnPointerPressed"
+                PointerReleased="OnPointerReleased"
+                PointerWheelChanged="OnPointerWheel"
+                BoxShadow="inset 0 0 15 0 #222">
+            <debugging:PointerDebugField IsVisible="{Binding #uc.ShowDebugLine}" Name="DebugField"
+                                         PointBrush="{DynamicResource ThemeBorderHighBrush}"
+                                         ScrollInfo="{Binding #uc.ScrollInfo}"/>
         </Border>
         <StackPanel Grid.Row="1" Orientation="Vertical" Margin="0, 5, 0, 0">
-            <TextBlock HorizontalAlignment="Right" Text="{Binding #DebugField.PointCount, StringFormat='Points: {0}'}" />
+            <StackPanel.Styles>
+                <Style Selector="TextBlock">
+                    <Setter Property="FontFamily" Value="monospace" />
+                </Style>
+                <Style Selector="TextBlock:not(.title)">
+                    <Setter Property="Margin" Value="3,0,0,0" />
+                </Style>
+                <Style Selector="TextBlock.title">
+                    <Setter Property="FontWeight" Value="Bold" />
+                    <Setter Property="Margin" Value="0, 5, 0, 0" />
+                </Style>
+                <Style Selector="TextBlock.separator">
+                    <Setter Property="Margin" Value="3, 3, 0, 0" />
+                </Style>
+                <Style Selector="TextBlock.invs">
+                    <Setter Property="Opacity" Value="0" />
+                </Style>
+            </StackPanel.Styles>
+            
+            <DockPanel>
+                <CheckBox IsChecked="{Binding #uc.ShowDebugLine, Mode=TwoWay}">Show debug lines</CheckBox>
+                
+                <TextBlock DockPanel.Dock="Right" HorizontalAlignment="Right" Text="{Binding #DebugField.PointCount, StringFormat='Pointer Events: {0}'}" />
+                <TextBlock DockPanel.Dock="Right" HorizontalAlignment="Right" Text="{Binding #uc.Performance.ScrollPolls, StringFormat='Scroll Events: {0}'}" Margin="0,0,5,0" />
+            </DockPanel>
+            
             <StackPanel Orientation="Vertical" Margin="5">
-                <TextBlock Text="{Binding #uc.PointerType, StringFormat='Pointer Type: {0}'}" />
-                <WrapPanel>
-                    <TextBlock Text="{Binding #uc.LastPoint.Pressure, StringFormat='Pressure: {0}'}" />
-                    <TextBlock Text="{Binding #uc.LastPoint.XTilt, StringFormat='X Tilt: {0}'}" />
-                    <TextBlock Text="{Binding #uc.LastPoint.YTilt, StringFormat='Y Tilt: {0}'}" />
-                </WrapPanel>
+
+                <Grid ColumnDefinitions="*, *, .5*" RowDefinitions="Auto, Auto, Auto">
+                    <StackPanel Orientation="Vertical">
+                        <TextBlock Text="Generic" Classes="title" />
+
+                        <TextBlock Text="{Binding #uc.PointerType, StringFormat='Pointer Type: {0}'}" />
+
+                        <TextBlock Classes="separator"
+                                   Text="{Binding #uc.LastPoint.IsLeftButtonPressed, StringFormat='Left Button Pressed: {0}; '}" />
+                        <TextBlock Text="{Binding #uc.LastPoint.IsMiddleButtonPressed, StringFormat='Middle Button Pressed: {0}; '}" />
+                        <TextBlock Text="{Binding #uc.LastPoint.IsRightButtonPressed, StringFormat='Right Button Pressed: {0};'}" />
+
+                        <TextBlock Classes="separator" Text="{Binding #uc.LastPoint.IsXButton1Pressed, StringFormat='X1 Button Pressed: {0}; '}" />
+                        <TextBlock Text="{Binding #uc.LastPoint.IsXButton2Pressed, StringFormat='X2 Button Pressed: {0};'}" />
+                    </StackPanel>
+                    <StackPanel Grid.Column="1" Grid.Row="0">
+                        <TextBlock Text="Pen Specific" Classes="title" />
+
+                        <TextBlock Text="{Binding #uc.LastPoint.Pressure, StringFormat='Pressure: {0:F2}; '}" />
+                        <TextBlock Classes="separator" Text="{Binding #uc.LastPoint.XTilt, StringFormat='X Tilt: {0:F2}°; '}" />
+                        <TextBlock Text="{Binding #uc.LastPoint.YTilt, StringFormat='Y Tilt: {0:F2}°;'}" />
+                        <TextBlock Text="{Binding #uc.LastPoint.Twist, StringFormat='Twist: {0:F2}°; '}" />
+                        <TextBlock Classes="separator" Text="{Binding #uc.LastPoint.IsInverted, StringFormat='Inverted: {0:F2}; '}" />
+
+                        <TextBlock Classes="separator" Text="{Binding #uc.LastPoint.IsEraser, StringFormat='Is Eraser: {0}; '}" />
+                        <TextBlock
+                            Text="{Binding #uc.LastPoint.IsBarrelButtonPressed, StringFormat='Barrel Button Pressed: {0}; '}" />
+
+                    </StackPanel>
+                    <StackPanel Grid.Column="2" Grid.Row="0">
+                        <TextBlock Text="Scrolling" Classes="title" />
+                        
+                        <TextBlock Text="{Binding #uc.ScrollInfo.TotalScroll, StringFormat='Total: {0:F2}; '}" />
+                        <TextBlock Text="{Binding #uc.ScrollInfo.Delta, StringFormat='Delta: {0:F2}; '}" />
+
+                        <TextBlock Text="{Binding #uc.ScrollInfo.Minimum, StringFormat='Min: {0:F2}; '}" />
+                        <TextBlock Text="{Binding #uc.ScrollInfo.Maximum, StringFormat='Max: {0:F2}; '}" />
+
+                    </StackPanel>
+                    
+                    <StackPanel Grid.Column="0" Grid.Row="1">
+                        <TextBlock Text="Pointer Performance" Classes="title" />
+
+                        <TextBlock
+                            Text="{Binding #uc.Performance.LastPollRate, StringFormat='Last Poll Rate: {0:F2}Hz; ', Mode=OneWay}" />
+                        <TextBlock
+                            Text="{Binding #uc.Performance.AveragePollRate, StringFormat='Average Poll Rate: {0:F2}Hz; ', Mode=OneWay}" />
+                    </StackPanel>
+                    
+                    <StackPanel Grid.Column="1" Grid.Row="1">
+                        <TextBlock Text="Scrolling Performance" Classes="title" />
+
+                        <TextBlock
+                            Text="{Binding #uc.Performance.LastScrollPoll, StringFormat='Last Poll Rate: {0:F2}Hz; ', Mode=OneWay}" />
+                        <TextBlock
+                            Text="{Binding #uc.Performance.AverageScrollPoll, StringFormat='Average Poll Rate: {0:F2}Hz; ', Mode=OneWay}" />
+                    </StackPanel>
+                    
+                    <TextBlock Text="Performance accuracy may be degraded due to debug lines"
+                               Foreground="{DynamicResource ErrorBrush}"
+                               Classes.invs="{Binding !#uc.ShowDebugLine}"
+                               Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="2"/>
+                </Grid>
+
+                <Grid ColumnDefinitions="*,*,0.5*">
+                </Grid>
+
             </StackPanel>
         </StackPanel>
     </Grid>

+ 132 - 5
src/PixiEditor.AvaloniaUI/Views/Dialogs/Debugging/PointerDebugPopup.axaml.cs

@@ -1,10 +1,15 @@
-using Avalonia;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Avalonia;
 using Avalonia.Input;
 
 namespace PixiEditor.AvaloniaUI.Views.Dialogs.Debugging;
 
 public partial class PointerDebugPopup : PixiEditorPopup
 {
+    private readonly Stopwatch pointerWatch = new();
+    private readonly Stopwatch scrollWatch = new();
+    
     public static readonly StyledProperty<PointerPointProperties> LastPointProperty =
         AvaloniaProperty.Register<PointerDebugPopup, PointerPointProperties>(nameof(LastPoint));
 
@@ -15,7 +20,7 @@ public partial class PointerDebugPopup : PixiEditorPopup
     }
     
     public static readonly StyledProperty<PointerType> PointerTypeProperty =
-        AvaloniaProperty.Register<PointerDebugPopup, PointerType>(nameof(LastPoint));
+        AvaloniaProperty.Register<PointerDebugPopup, PointerType>(nameof(PointerType));
 
     public PointerType PointerType
     {
@@ -23,21 +28,143 @@ public partial class PointerDebugPopup : PixiEditorPopup
         set => SetValue(PointerTypeProperty, value);
     }
     
+    public static readonly StyledProperty<PerformanceStats> PerformanceProperty =
+        AvaloniaProperty.Register<PointerDebugPopup, PerformanceStats>(nameof(Performance));
+
+    public PerformanceStats Performance
+    {
+        get => GetValue(PerformanceProperty);
+        set => SetValue(PerformanceProperty, value);
+    }
+
+    public static readonly StyledProperty<bool> ShowDebugLineProperty =
+        AvaloniaProperty.Register<PointerDebugPopup, bool>(nameof(ShowDebugLine), defaultValue: true);
+
+    public bool ShowDebugLine
+    {
+        get => GetValue(ShowDebugLineProperty);
+        set => SetValue(ShowDebugLineProperty, value);
+    }
+
+    public static readonly StyledProperty<ScrollStats> ScrollInfoProperty =
+        AvaloniaProperty.Register<PointerDebugPopup, ScrollStats>(nameof(ScrollInfo));
+
+    public ScrollStats ScrollInfo
+    {
+        get => GetValue(ScrollInfoProperty);
+        set => SetValue(ScrollInfoProperty, value);
+    }
+
     public PointerDebugPopup()
     {
         InitializeComponent();
     }
 
+    private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
+    {
+        UpdateProperties(e);
+    }
+
+    private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
+    {
+        UpdateProperties(e);
+    }
+
+    private void OnPointerEntered(object? sender, PointerEventArgs e)
+    {
+        DebugField.ClearPoints();
+        Performance = default;
+        ScrollInfo = default;
+    }
+
+    private void OnPointerWheel(object? sender, PointerWheelEventArgs e)
+    {
+        var current = ScrollInfo;
+        var total = current.TotalScroll + e.Delta;
+
+        var soonTm = new ScrollStats
+        {
+            TotalScroll = total,
+            Delta = e.Delta,
+            Minimum = new Vector(Math.Min(current.Minimum.X, e.Delta.X), Math.Min(current.Minimum.Y, e.Delta.Y)),
+            Maximum = new Vector(Math.Max(current.Maximum.X, e.Delta.X), Math.Max(current.Maximum.Y, e.Delta.Y))
+        };
+
+        ScrollInfo = soonTm;
+
+        var time = scrollWatch.Elapsed;
+        scrollWatch.Restart();
+
+        if (time.TotalMilliseconds != 0 && time.TotalMilliseconds < 1000)
+        {
+            var lastPerformance = Performance;
+            double rate = 1000 / time.TotalMilliseconds;
+
+            lastPerformance.LastScrollPoll = rate;
+            lastPerformance.ScrollPolls++;
+            lastPerformance.AverageScrollPoll = ((lastPerformance.ScrollPolls - 1) * lastPerformance.AverageScrollPoll + rate) / lastPerformance.ScrollPolls;
+
+            Performance = lastPerformance;
+        }
+    }
+
     private void OnPointerMoved(object? sender, PointerEventArgs e)
     {
+        var time = pointerWatch.Elapsed;
+        pointerWatch.Restart();
+
+        if (time.TotalMilliseconds != 0 && time.TotalMilliseconds < 250)
+        {
+            var lastPerformance = Performance;
+            double rate = 1000 / time.TotalMilliseconds;
+
+            lastPerformance.LastPollRate = rate;
+            lastPerformance.CountedPolls++;
+            lastPerformance.AveragePollRate = ((lastPerformance.CountedPolls - 1) * lastPerformance.AveragePollRate + rate) / lastPerformance.CountedPolls;
+
+            Performance = lastPerformance;
+        }
+
+        var point = UpdateProperties(e);
+        
+        if (ShowDebugLine)
+        {
+            DebugField.ReportPoint(point);
+        }
+    }
+
+    private PointerPoint UpdateProperties(PointerEventArgs e)
+    {
+        PointerType = e.Pointer.Type;
         var point = e.GetCurrentPoint(DebugField);
         LastPoint = point.Properties;
+
+        return point;
+    }
+
+    public struct PerformanceStats
+    {
+        public double LastPollRate { get; set; }
+        
+        public int CountedPolls { get; set; }
+        
+        public double AveragePollRate { get; set; }
+        
+        public double LastScrollPoll { get; set; }
         
-        DebugField.ReportPoint(point);
+        public int ScrollPolls { get; set; }
+
+        public double AverageScrollPoll { get; set; }
     }
 
-    private void OnPointerEntered(object? sender, PointerEventArgs e)
+    public struct ScrollStats
     {
-        DebugField.ClearPoints();
+        public Vector TotalScroll { get; set; }
+        
+        public Vector Delta { get; set; }
+        
+        public Vector Minimum { get; set; }
+        
+        public Vector Maximum { get; set; }
     }
 }

+ 3 - 0
src/PixiEditor.AvaloniaUI/Views/Main/MainTitleBar.axaml

@@ -260,6 +260,9 @@
                 <MenuItem
                     ui:Translator.Key="OPEN_LOCALIZATION_DEBUG_WINDOW"
                     xaml:Menu.Command="PixiEditor.Debug.OpenLocalizationDebugWindow"/>
+                <MenuItem
+                    Header="Open pointer debug window"
+                    xaml:Menu.Command="PixiEditor.Debug.OpenPointerDebugWindow"/>
                 <Separator/>
                 <MenuItem
                     ui:Translator.Key="OPEN_LOCAL_APPDATA_DIR"