Browse Source

NumberInput mouse grabber

flabbet 1 year ago
parent
commit
b04c583b51

+ 82 - 0
src/PixiEditor.AvaloniaUI/Views/Input/NumberInput.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Text.RegularExpressions;
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
 using Avalonia.Data;
 using Avalonia.Input;
 using Avalonia.Interactivity;
@@ -31,6 +32,8 @@ internal partial class NumberInput : TextBox
     public static readonly StyledProperty<string> FormattedValueProperty = AvaloniaProperty.Register<NumberInput, string>(
         nameof(FormattedValue), "0");
 
+    public static readonly StyledProperty<bool> EnableScrollChangeProperty = AvaloniaProperty.Register<NumberInput, bool>(
+        "EnableScrollChange", true);
     public string FormattedValue
     {
         get => GetValue(FormattedValueProperty);
@@ -110,8 +113,21 @@ internal partial class NumberInput : TextBox
         'i', 'n', 'f', 't', 'y', 'e', 'I', 'N', 'F', 'T', 'Y', 'E'
     };
 
+
     protected override Type StyleKeyOverride => typeof(TextBox);
 
+    public bool EnableScrollChange
+    {
+        get { return (bool)GetValue(EnableScrollChangeProperty); }
+        set { SetValue(EnableScrollChangeProperty, value); }
+    }
+
+    private Control? leftGrabber;
+    private Control? rightGrabber;
+    
+    private double _pressedValue;
+    private double _pressedRelativeX;
+    
     static NumberInput()
     {
         ValueProperty.Changed.Subscribe(OnValueChanged);
@@ -140,6 +156,67 @@ internal partial class NumberInput : TextBox
         VerticalAlignment = VerticalAlignment.Center;
     }
 
+    protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+    {
+        base.OnApplyTemplate(e);
+        
+        InnerLeftContent = leftGrabber = CreateMouseGrabber(); 
+        leftGrabber.HorizontalAlignment = HorizontalAlignment.Left;
+        InnerRightContent = rightGrabber = CreateMouseGrabber(); 
+        rightGrabber.HorizontalAlignment = HorizontalAlignment.Right;
+    }
+
+    protected override void OnSizeChanged(SizeChangedEventArgs e)
+    {
+        if (e.NewSize.Width < 100)
+        {
+            rightGrabber.IsVisible = false;
+        }
+        
+        leftGrabber.Height = e.NewSize.Height - 10;
+        leftGrabber.Width = e.NewSize.Width / 4f;
+        
+        rightGrabber.Height = e.NewSize.Height - 10;
+        rightGrabber.Width = e.NewSize.Width / 4f;
+    }
+
+    private Control CreateMouseGrabber()
+    {
+        var grabber = new Grid()
+        {
+            Cursor = new Cursor(StandardCursorType.SizeWestEast),
+            Background = Brushes.Transparent,
+        };
+
+        grabber.PointerPressed += GrabberPressed;
+        grabber.PointerMoved += GrabberMoved;
+        
+        return grabber;
+    }
+    
+    private void GrabberPressed(object sender, PointerPressedEventArgs e)
+    {
+        e.Pointer.Capture(leftGrabber);
+        _pressedValue = Value;
+        _pressedRelativeX = e.GetPosition(this).X;
+        e.Handled = true;
+    }
+    
+    private void GrabberMoved(object sender, PointerEventArgs e)
+    {
+        if(e.Pointer.Captured != null && (e.Pointer.Captured.Equals(leftGrabber) || e.Pointer.Captured.Equals(rightGrabber)))
+        {
+            double relativeX = e.GetPosition(this).X;
+            double diff = relativeX - _pressedRelativeX;
+
+            double pixelsPerUnit = 5;
+            
+            double newValue = _pressedValue + diff / pixelsPerUnit;
+            Value = (float)Math.Round(Math.Clamp(newValue, Min, Max), Decimals);
+            e.Handled = true; 
+        }
+    }
+
     private void BindTextBoxBehavior(TextBoxFocusBehavior behavior)
     {
         Binding focusNextBinding = new Binding(nameof(FocusNext))
@@ -258,6 +335,11 @@ internal partial class NumberInput : TextBox
 
     protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
     {
+        if (!EnableScrollChange)
+        {
+            return;
+        }
+        
         int step = (int)e.Delta.Y;
 
         double newValue = Value;

+ 20 - 20
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/ColorMatrixPropertyView.axaml

@@ -24,26 +24,26 @@
             <TextBlock Grid.Row="0" Grid.Column="4" Text="A" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" TextAlignment="Center" />
             <TextBlock Grid.Row="0" Grid.Column="5" Text="+" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" TextAlignment="Center" />
             
-            <input:NumberInput Grid.Row="1" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M11, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="1" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M12, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="1" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M13, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="1" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M14, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="1" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M15, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="2" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M21, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="2" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M22, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="2" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M23, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="2" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M24, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="2" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M25, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="3" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M31, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="3" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M32, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="3" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M33, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="3" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M34, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="3" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M35, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="4" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M41, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="4" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M42, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="4" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M43, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="4" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M44, Mode=TwoWay}" />
-            <input:NumberInput Grid.Row="4" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M45, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="1" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M11, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="1" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M12, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="1" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M13, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="1" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M14, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="1" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M15, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="2" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M21, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="2" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M22, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="2" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M23, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="2" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M24, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="2" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M25, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="3" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M31, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="3" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M32, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="3" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M33, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="3" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M34, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="3" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M35, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="4" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M41, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="4" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M42, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="4" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M43, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="4" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M44, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" Grid.Row="4" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M45, Mode=TwoWay}" />
         </Grid>
     </StackPanel>
 </properties:NodePropertyView>

+ 2 - 1
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/DoublePropertyView.axaml

@@ -11,6 +11,7 @@
                              x:Class="PixiEditor.AvaloniaUI.Views.Nodes.Properties.DoublePropertyView">
     <Grid HorizontalAlignment="{Binding IsInput, Converter={converters:BoolToValueConverter FalseValue='Right', TrueValue='Stretch'}}">
         <TextBlock VerticalAlignment="Center" ui:Translator.Key="{Binding DisplayName}"/>
-        <input:NumberInput HorizontalAlignment="Right" MinWidth="100" Decimals="6" IsVisible="{Binding ShowInputField}" Value="{Binding Value, Mode=TwoWay}" />
+        <input:NumberInput EnableScrollChange="False" 
+                           HorizontalAlignment="Right" MinWidth="100" Decimals="6" IsVisible="{Binding ShowInputField}" Value="{Binding Value, Mode=TwoWay}" />
     </Grid>
 </properties:NodePropertyView>

+ 2 - 1
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/Int32PropertyView.axaml

@@ -10,6 +10,7 @@
                              x:Class="PixiEditor.AvaloniaUI.Views.Nodes.Properties.Int32PropertyView">
     <Grid HorizontalAlignment="{Binding IsInput, Converter={converters:BoolToValueConverter FalseValue='Right', TrueValue='Stretch'}}">
         <TextBlock VerticalAlignment="Center" ui:Translator.Key="{Binding DisplayName}"/>
-        <input:NumberInput HorizontalAlignment="Right" MinWidth="100" IsVisible="{Binding ShowInputField}" Value="{Binding Value, Mode=TwoWay}" />
+        <input:NumberInput EnableScrollChange="False" Decimals="0"
+            HorizontalAlignment="Right" MinWidth="100" IsVisible="{Binding ShowInputField}" Value="{Binding Value, Mode=TwoWay}" />
     </Grid>
 </properties:NodePropertyView>

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/KernelPropertyView.axaml

@@ -38,7 +38,7 @@
         <ItemsControl ItemsSource="{Binding ReferenceCollections}" Margin="0,1">
             <ItemsControl.ItemTemplate>
                 <DataTemplate>
-                    <input:NumberInput Value="{Binding Value, Mode=TwoWay}" Decimals="4" />
+                    <input:NumberInput EnableScrollChange="False" Value="{Binding Value, Mode=TwoWay}" Decimals="4" />
                 </DataTemplate>
             </ItemsControl.ItemTemplate>
             <ItemsControl.ItemsPanel>

+ 2 - 2
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/VecDPropertyView.axaml

@@ -11,8 +11,8 @@
     <StackPanel HorizontalAlignment="{Binding IsInput, Converter={converters:BoolToValueConverter FalseValue='Right', TrueValue='Stretch'}}">
         <TextBlock VerticalAlignment="Center" ui:Translator.Key="{Binding DisplayName}"/>
         <StackPanel IsVisible="{Binding ShowInputField}">
-            <input:NumberInput MinWidth="100" Value="{Binding XValue, Mode=TwoWay}" Margin="0,2" />
-            <input:NumberInput MinWidth="100" Value="{Binding YValue, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" MinWidth="100" Value="{Binding XValue, Mode=TwoWay}" Margin="0,2" />
+            <input:NumberInput EnableScrollChange="False" MinWidth="100" Value="{Binding YValue, Mode=TwoWay}" />
         </StackPanel>
     </StackPanel>
 </properties:NodePropertyView>

+ 2 - 2
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/VecIPropertyView.axaml

@@ -11,8 +11,8 @@
     <StackPanel HorizontalAlignment="{Binding IsInput, Converter={converters:BoolToValueConverter FalseValue='Right', TrueValue='Stretch'}}">
         <TextBlock VerticalAlignment="Center" ui:Translator.Key="{Binding DisplayName}"/>
         <StackPanel IsVisible="{Binding ShowInputField}">
-            <input:NumberInput MinWidth="100" Decimals="0" Value="{Binding XValue, Mode=TwoWay}" Margin="0,2" />
-            <input:NumberInput MinWidth="100" Decimals="0" Value="{Binding YValue, Mode=TwoWay}" />
+            <input:NumberInput EnableScrollChange="False" MinWidth="100" Decimals="0" Value="{Binding XValue, Mode=TwoWay}" Margin="0,2" />
+            <input:NumberInput EnableScrollChange="False" MinWidth="100" Decimals="0" Value="{Binding YValue, Mode=TwoWay}" />
         </StackPanel>
     </StackPanel>
 </properties:NodePropertyView>